• This notebook summarizes the results from cvtkpy (Genome-wide temporal covariances of allele frequencies).
  • The covariance window size used is 100k (but the window sizes did not affect the results)

1 Plots COVs results

  • CIs from the ‘standard’ bootstrap method
pops<-c("PWS","TB","SS")
covs<-data.frame()
for (p in 1: length(pops)){
    #covariance output file
    cov<-read.csv(paste0("../cvtk_analysis/MD2000_3pops_maf05_temp_cov_matrix_",pops[p],"_100k.csv"))
    cov<-cov[,-1]
        
    #reshape the matrix
    mat1<-cov[1:3,]
    mat2<-cov[4:6,]
        
    covdf<-data.frame()
    k=1
    for (i in 1:nrow(mat1)){
        for (j in 1:ncol(mat1)){
            covdf[k,1]<-mat2[i,j]
            covdf[k,2]<-mat1[i,j]
            k=k+1
        }
    }
    colnames(covdf)<-c("label","value")
    covdf$value<-as.numeric(covdf$value)
    covar<-covdf[grep("cov",covdf$label),]
        
    #remove the redundant values
    if (pops[p]!="SS") covar<-covar[!duplicated(covar[, 2]),] 
    if (pops[p]=="SS") covar<-covar[c(1,2,4),]
        
    #assign the starting time period and covering period values
    covar$year<-c(1,2,2)
    covar$series<-c("1991","1991","1996")
        
    #assign population name
    covar$location<-pops[p]
    
    #combine in to one matrix
    covs<-rbind(covs, covar)
}

covs$time<-rep(c("cov12","cov13","cov23"), 3)
colnames(covs)[2]<-"cov"

# 95% confidence intervals (calculated from the 'straps' returned from bootstrap_cov2() ci=1.96*sd(straps))
time<-c("cov12","cov13","cov23")

covs$ci<-NA
for (i in 1:length(pops)){  
     if (i!=3){
        df<-read.csv(paste0("../cvtk_analysis/MD2000_",pops[i],"_CIs_100kwindow.csv"), header=F)
        covs$ci[covs$location==pops[i]&time=='cov12']<-df[1,2]
        covs$ci[covs$location==pops[i]&time=='cov13']<-df[1,3]
        covs$ci[covs$location==pops[i]&time=='cov23']<-df[2,3]
    }
    if (p==3) {
        df<-read.csv(paste0("../cvtk_analysis/MD2000_",pops[i],"_CIs_100kwindow.csv"), header=F)
        covs$ci[covs$location==pops[i]&time=='cov23']<-df[1,2] 
    }
}

write.csv(covs,"../Output/COV_analysis/MD2000_GW_covariance_CIs.csv")

xtexts<-c("\u03941991-1996\n ~ \u03941996-2006", "\n  ~ \u03942006-2017")

ggplot(data=covs, aes(x=year, y=cov, color=location, shape=series, group=interaction(location, series)))+
        geom_point(size=3, position=position_dodge(width = 0.1,preserve ="total"))+
        geom_line(data=covs, aes(x=year, y=cov,color=location, group=interaction(location, series)), position=position_dodge(width = 0.1,preserve ="total"))+
        ylab("Covariance")+xlab('')+theme_classic()+
        theme(legend.title = element_blank())+
        geom_hline(yintercept = 0,color="gray70", size=0.3)+
        geom_errorbar(aes(ymin=cov-ci, ymax=cov+ci), width=.2, size=.2, position=position_dodge(width = 0.1,preserve ="total"))+
        scale_shape_manual(values=c(16,17),labels=c("\u0394'91-'96~","\u0394'96-'06~"))+
        scale_x_continuous(breaks = c(1,2), labels=xtexts)+
        scale_color_manual(values=cols[c(2,3,1)])+ylim(-0.0037,0.0013)
ggsave(paste0("../Output/COV_analysis/MD2000_3Pops_Cov_overtime_CIestimated.png"),width = 4.7, height = 3, dpi=300)
    
covs$time<-factor(covs$time, levels=c("cov12","cov23","cov13"))
xtexts<-c("\u0394'91-'96\n ~ \u0394'96-'06", "\u0394'96-'06\n  ~ \u0394'06-'17", "\u0394'91-'96\n  ~ \u0394'06-'17")

ggplot(data=covs, aes(x=time, y=cov, color=location))+
        geom_point(size=3, position=position_dodge(width = 0.1,preserve ="total"))+
        ylab("Covariance")+xlab('')+theme_classic()+
        theme(legend.title = element_blank(), axis.text.x = element_text(size=9))+
        geom_hline(yintercept = 0,color="gray70", size=0.3)+
        geom_errorbar(aes(ymin=cov-ci, ymax=cov+ci), width=.2, size=.2, position=position_dodge(width = 0.1,preserve ="total"))+
        scale_x_discrete(labels=xtexts)+
    scale_color_manual(values=cols[c(2,3,1)])+
    geom_vline(xintercept = c(1.5,2.5), color="gray", size=0.2)+ylim(-0.0038,0.0014)
ggsave(paste0("../Output/COV_analysis/MD2000_3Pops_Cov_CI_3timepoints.png"),width = 4.57, height = 3, dpi=300)

  • Same results but plot differently

2 Find regions with high covariances (outlier regions) in each population

2.1 Plot the covariances across the genome

#Find the regions with a high temporal covariance 
pops<-c("PWS","TB","SS")
evens<-paste0("chr",seq(2,26, by=2))
cov.list<-list()
covs_all<-list()
k=1
for (p in 1: length(pops)){
    pop<-pops[p]
    iv<-read.csv(paste0("../cvtk_analysis/3pops_intervals_100kwindow.csv"), row.names = 1)
    if (p==3) {
        cov23<-read.csv(paste0("../cvtk_analysis/",pop,"_cov23_MD2000_100kwindow.csv"), header = F)
        covs<-cbind(iv, cov23)
        colnames(covs)[4]<-c("cov23")
        covs$index=1:nrow(covs)
        covs$color<-"col1"
        covs$color[covs$chrom %in% evens]<-"col2"

        covs[sapply(covs, is.infinite)] <- NA
        covs[sapply(covs, is.nan)] <- NA
        
        cov.list[[k]]<-covs
        names(cov.list)[k]<-pop
        k=k+1
            
        y<-min(covs$cov23, na.rm=T)
        ymin<-ifelse (y<=-0.1,-0.1, y) 
        ymax<-max(covs$cov23, na.rm=T)
        ggplot(covs, aes(x=index, y=cov23, color=color))+
            geom_point(size=1, alpha=0.5)+
            theme_classic()+
            ylim(ymin,ymax)+
            scale_color_manual(values=c("gray70","steelblue"), guide="none")+
            ylab("Covariance")+xlab('Chromosome')+
            theme(axis.text.x = element_blank())+
            ggtitle(pop)
        ggsave(paste0("../Output/COV_analysis/",pop,"_MD2000_tempCovs_acrossGenome_100kWindow.png"), width = 8, height = 2.7, dpi=300) 
        }
    else {
        cov12<-read.csv(paste0("../cvtk_analysis/",pop,"_cov12_MD2000_100kwindow.csv"), header = F)
        cov23<-read.csv(paste0("../cvtk_analysis/",pop,"_cov23_MD2000_100kwindow.csv"), header = F)
        cov13<-read.csv(paste0("../cvtk_analysis/",pop,"_cov13_MD2000_100kwindow.csv"), header = F)
        covs<-cbind(iv, cov12, cov23,cov13)
        colnames(covs)[4:6]<-c("cov12","cov23","cov13")
        covs$index=1:nrow(covs)
    
        covs$color<-"col1"
        covs$color[covs$chrom %in% evens]<-"col2"
    
        covs[sapply(covs, is.infinite)] <- NA
        covs[sapply(covs, is.nan)] <- NA
        
        cov.list[[k]]<-covs
        names(cov.list)[k]<-pop
        k=k+1
        covsm<-melt(covs[,c("index","color","cov12","cov23","cov13")], id.vars = c("index", "color"))
        ymax<-max(covsm$value, na.rm=T)
        y<-min(covsm$value, na.rm=T)
        ymin<-ifelse (y<=-0.1,-0.1, y) 
        ggplot(covsm, aes(x=index, y=value, color=color))+
            facet_wrap(~variable, nrow=3)+
            geom_point(size=1, alpha=0.5)+
            theme_classic()+
            ylim(ymin,ymax)+
            scale_color_manual(values=c("gray70","steelblue"), guide="none")+
            ylab("Covariance")+xlab('Chromosome')+
            theme(axis.text.x = element_blank())+
            ggtitle(paste0(pop))
        ggsave(paste0("../Output/COV_analysis/",pop,"_MD2000_tempCovs_acrossGenome_100kWindow.png"), width = 8, height = 8, dpi=300)    
    }
}

3 Find the covariance lower cut off values for outlier analysis (top1%)

3.1 Plot the outlier ranges

# Top 1% covariance values for all Pop x Time points
cv<-c("cov12","cov13","cov23")
cvrange<-data.frame(pop=c(paste0(pops[1:2],"_", cv[1]),paste0(pops[1:2],"_", cv[2]),paste0(pops,"_", cv[3])))
k=1
for (i in 1:length(cv)){
    if (i==1|i==2){
        if (i==1) k=1
        if (i==2) k=3
        #PWS
        df1<-cov.list[[paste0("PWS")]]
        df1<-df1[order(df1[,cv[i]], decreasing=T),]
        n<-ceiling(nrow(df1)*0.01) #top1% region
        df1$top1<-"N"
        df1$top1[1:n]<-"PWS"
        rg<-range(df1[df1$top1=="PWS",cv[i]], na.rm=T)
        cvrange[k,"100k"]<-paste0(rg[1],"-",rg[2])
          
        #tb
        df2<-cov.list[["TB"]]
        df2<-df2[order(df2[,cv[i]], decreasing=T),]
        df2$top1<-"N"
        df2$top1[1:n]<-"TB"
        rg2<-range(df2[df2$top1=="TB", cv[i]], na.rm=T)
        cvrange[(k+1),"100k"]<-paste0(rg2[1],"-",rg2[2])
    }
   
    if (i==3){
        k=5
        #pws
        df1<-cov.list[["PWS"]]
        df1<-df1[,c("chrom","start","end","cov23")]
        df1<-df1[order(df1$cov23, decreasing=T),]
        n<-ceiling(nrow(df1)*0.01) #top1% region
        df1$top1<-"N"
        df1$top1[1:n]<-"PWS"
        
        rg<-range(df1[df1$top1=="PWS",cv[i]], na.rm=T)
        cvrange[k,"100k"]<-paste0(rg[1],"-",rg[2])
           
        #tb
        df2<-cov.list[["TB"]]
        df2<-df2[,c("chrom","start","end","cov23")]
        df2<-df2[order(df2$cov23, decreasing=T),]
        df2$top1<-"N"
        df2$top1[1:n]<-"TB"
        rg2<-range(df2[df2$top1=="TB", cv[i]], na.rm=T)
        cvrange[(k+1),"100k"]<-paste0(rg2[1],"-",rg2[2])
    
        #ss
        df3<-cov.list[["SS"]]
        df3<-df3[,c("chrom","start","end","cov23")]
        df3<-df3[order(df3$cov23, decreasing=T),]
        df3$top1<-"N"
        df3$top1[1:n]<-"SS"
        rg3<-range(df3[df3$top1=="SS", cv[i]], na.rm=T)
        cvrange[(k+2),"100k"]<-paste0(rg3[1],"-",rg3[2])
    }
}


cvs<-melt(cvrange, id.vars = "pop")
cvs<-cvs %>%
  separate(value, c("low", "high"), "-")
cvs$low<-as.numeric(cvs$low)
cvs$high<-as.numeric(cvs$high)
cvs<-cvs%>%
  separate(pop, c("pop", "cov"), "_")

ggplot(cvs, aes(x=cov, y=low, color=pop))+
    geom_point(size=3)+
    ylab("Lower limit of top 1% covariance")+
    theme_light()+xlab("")+
    geom_vline(xintercept=c(1.5,2.5), color="gray")+scale_color_manual(values=cols[c(2,3,1)])+
    theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(), legend.title=element_blank())
ggsave("../Output/COV_analysis/TempCov_Range_lowLimit_comparison_MD2000.png", width = 5, height = 3, dpi=300)

3.2 Use the highest values for each time period to define outlier regions

lows<-aggregate(cvs$low, by=list(cvs$cov), max)
names(lows)<-c("cov","low")
#low cutoff for each time period (100k-window)
#   cov        low
#1 cov12 0.02985278
#2 cov13 0.03558964
#3 cov23 0.03419953
write.csv(lows, "../Output/COV_analysis/Cutoff_covs_max.csv", row.names = F)

# Outliers based on the new low cut-off values 100k window. 
cov12<-data.frame()
cov23<-data.frame()
cov13<-data.frame()

for (i in 1:length(cov.list)){
 #PWS and TB
  if (i==1|i==2){
    covs<-cov.list[[i]]
    pop<-names(cov.list)[i]
  
    #outlier cutoff value
    x<-lows$low[lows$cov=="cov12"]
    covs12_top<-subset(covs, cov12>=x)
    covs12_top<-covs12_top[order(covs12_top$chrom, covs12_top$start),]
    covs12_top$pop<-pop
    cov12<-rbind(cov12, covs12_top)
    
    covs<-covs[order(covs$cov13, decreasing=T),]
    x<-lows$low[lows$cov=="cov13"]
    covs13_top<-subset(covs, cov13>=x)
    covs13_top<-covs13_top[order(covs13_top$chrom, covs13_top$start),]
    covs13_top$pop<-pop
    cov13<-rbind(cov13, covs13_top)
    
    covs<-covs[order(covs$cov23, decreasing=T),]
    x<-lows$low[lows$cov=="cov23"]
    covs23_top<-subset(covs[,c("chrom","start","end","cov23","index","color")], cov23>=x)
    covs23_top<-covs23_top[order(covs23_top$chrom, covs23_top$start),]
    covs23_top$pop<-pop
    cov23<-rbind(cov23, covs23_top)
 }
 if (grepl("SS",names(cov.list)[i])){
    covs<-cov.list[[i]]
    
    pop<-gsub("_.+",'', names(cov.list)[i])
    win<-gsub(paste0(pop,"_"), '', names(cov.list)[i])
    
    covs<-covs[order(covs$cov23, decreasing=T),]
    x<-lows$low[lows$cov=="cov23"]
    covs23_top<-subset(covs, cov23>=x)
    covs23_top<-covs23_top[order(covs23_top$chrom, covs23_top$start),]
    covs23_top$pop<-pop
    cov23<-rbind(cov23, covs23_top)
    }
}

write.csv(cov12, "../Output/COV_analysis/3pops_top1percent_outlier_cutoff.cov12.csv",row.names = F)
write.csv(cov23, "../Output/COV_analysis/3pops_top1percent_outlier_cutoff.cov23.csv",row.names = F)
write.csv(cov13, "../Output/COV_analysis/3pops_top1percent_outlier_cutoff.cov13.csv",row.names = F)

3.3 Create plots with different colors for outliers

3.3.1 Each time period

#for COV12 and COV13 for TB and PWS (100K)
cv<-c("cov12","cov13","cov23")
for (i in 1:length(cv)){
    if (i==1|i==2){
        #cutoff value
        x<-lows$low[lows$cov==cv[i]]
        #PWS
        df1<-cov.list[["PWS"]]
        df1<-df1[order(df1[,cv[i]], decreasing=T),]
        df1$top1<-"N"
        df1$top1[df1[,cv[i]]>=x]<-"PWS"
        
        #tb
        df2<-cov.list[["TB"]]
        df2<-df2[order(df2[,cv[i]], decreasing=T),]
        df2$top1<-"N"
        df2$top1[df2[,cv[i]]>=x]<-"TB"
        
        #Combine PWS and TB tables
        co<-rbind(df1, df2)
        co$chrom<-factor(co$chrom, levels=paste0("chr", 1:26))
        co$top1<-factor(co$top1, levels=c("PWS","TB","N"))
        colnames(co)[which(colnames(co)==cv[i])]<-"cov"
    
        ymax<-max(co$cov, na.rm=T)
        #assign colors
        co$top1<-apply(co, 1, function(x) {ifelse (x['top1']=="N", x['color'], x['top1'])} )
        co$top1<-factor(co$top1, levels=c("PWS","TB","col1","col2"))
        
        #count the number of sites per chromosomes
        poss<-data.frame(chr=paste0("chr",1:26))
        k=1
        for (j in 1:26){
            df<-df1[df1$chr==paste0("chr",j),]
            poss$start[j]<-k
            poss$end[j]<-k+nrow(df)-1
            k=k+nrow(df)
        }
        poss$x<-poss$start+(poss$end-poss$start)/2
        ymax<-max(co$cov, na.rm=T)
        ggplot(co, aes(x=index, y=cov, color=top1))+
            geom_point(size=0.5)+
            theme_classic()+ylim(-0.1,ymax)+
            scale_color_manual(values=c(paste0(cols[2],"B3"),paste0(cols[1],"B3"),"#A8BBCD66","#D6D6D666"), labels=c("PWS", "TB", "",""))+
            ylab("Covariance")+
            ggtitle(paste0("100k window ",cv[i]))+
            guides(color = guide_legend(override.aes = list(color=c(cols[2], cols[1],"white","white"), size=2), title=element_text("Outlier Region", size=10)))+
            scale_x_continuous(name="Chromosome", breaks=poss$x, labels=1:26)
        ggsave(paste0("../Output/COV_analysis/3Pops.",cv[i],"_100k_Window_Outliers.png"), width = 10, height = 3.5, dpi=300)
    }
   
    if (i==3){
       #cutoff value
        x<-lows$low[lows$cov==cv[i]]
        #PWS
        df1<-cov.list[["PWS"]]
        df1<-df1[,c("chrom","start","end","cov23","index","color")]
        df1<-df1[order(df1$cov23, decreasing=T),]
        df1$top1<-"N"
        df1$top1[df1[,cv[i]]>=x]<-"PWS"
        
        #tb
        df2<-cov.list[["TB"]]
        df2<-df2[,c("chrom","start","end","cov23","index","color")]
        df2<-df2[order(df2$cov23, decreasing=T),]
        df2$top1<-"N"
        df2$top1[df2[,cv[i]]>=x]<-"TB"
    
        #ss
        df3<-cov.list[["SS"]]
        df3<-df3[,c("chrom","start","end","cov23","index","color")]
        df3<-df3[order(df3$cov23, decreasing=T),]
        df3$top1<-"N"
        df3$top1[df3[,cv[i]]>=x]<-"SS"

        co<-rbind(df1,df2,df3)

        co$chrom<-factor(co$chrom, levels=paste0("chr", 1:26))
        co$top1<-factor(co$top1, levels=c("PWS","TB","SS","N"))
        ymax<-max(co$cov23, na.rm=T)
        
        #assign colors
        co$top1<-apply(co, 1, function(x) {ifelse (x['top1']=="N", x['color'], x['top1'])} )
        co$top1<-factor(co$top1, levels=c("PWS","TB","SS","col1","col2"))
        #count the number of sites per chromosomes
        poss<-data.frame(chr=paste0("chr",1:26))
        k=1
        for (j in 1:26){
            df<-df1[df1$chr==paste0("chr",j),]
            poss$start[j]<-k
            poss$end[j]<-k+nrow(df)-1
            k=k+nrow(df)
        }
        poss$x<-poss$start+(poss$end-poss$start)/2
        ymax<-max(co$cov, na.rm=T)
        ggplot(co, aes(x=index, y=cov23, color=top1))+
            geom_point(size=0.5)+
            theme_classic()+ylim(-0.1,ymax)+
            scale_color_manual(values=c(paste0(cols[c(2,1,3)],"B3"),"#A8BBCD66","#D6D6D666"), labels=c("PWS", "TB","SS", "",""))+
                ylab("Covariance")+
                ggtitle(paste0(" 100k window ",cv[i]))+
                guides(color = guide_legend(override.aes = list(color=c(cols[c(2,1,3)],"white","white"), size=2), title=element_text("Outlier (1%)")))+
            scale_x_continuous(name="Chromosome", breaks=poss$x, labels=1:26)+
            theme(legend.title = element_text(size=10))
        ggsave(paste0("../Output/COV_analysis/3Pops.cov23_100k_Window_Outliers.png"), width = 10, height = 3.5, dpi=300)
        }
}

3.3.2 All time priods for PWS and TB

## Plot 3 time periods together for PWS and TB
Cov<-data.frame()
for (i in 1:length(cv)){
    #cutoff value
    x<-lows$low[lows$cov==cv[i]]
    #PWS
    df1<-cov.list[["PWS"]]
    df1<-df1[order(df1[,cv[i]], decreasing=T),]
    df1$top1<-"N"
    df1$top1[df1[,cv[i]]>=x]<-"PWS"
    
    #tb
    df2<-cov.list[["TB"]]
    df2<-df2[order(df2[,cv[i]], decreasing=T),]
    df2$top1<-"N"
    df2$top1[df2[,cv[i]]>=x]<-"TB"
    
    #Combine PWS and TB tables
    co<-rbind(df1, df2)
    co$chrom<-factor(co$chrom, levels=paste0("chr", 1:26))
    colnames(co)[which(colnames(co)==cv[i])]<-"cov"
    #assgin colors
    co$top1<-apply(co, 1, function(x) {ifelse (x['top1']=="N", x['color'], x['top1'])} )
    co$top1<-factor(co$top1, levels=c("PWS","TB","col1","col2"))
    co$time<-cv[i]
    
    Cov<-rbind(Cov, co[,c("index", "cov","top1","time")])
}

#count the number of sites per chromosomes
df1<-cov.list[["PWS"]]
poss<-data.frame(chr=paste0("chr",1:26))
k=1
for (j in 1:26){
        df<-df1[df1$chr==paste0("chr",j),]
        poss$start[j]<-k
        poss$end[j]<-k+nrow(df)-1
        k=k+nrow(df)
}
poss$x<-poss$start+(poss$end-poss$start)/2
ymax<-max(co$cov, na.rm=T)
ggplot(Cov, aes(x=index, y=cov, color=top1))+
    facet_wrap(~time, ncol=1)+
    geom_point(size=0.5)+
    theme_classic()+ylim(-0.1,ymax)+
    scale_color_manual(values=c(paste0(cols[c(2,1)],"B3"),"#A8BBCD66","#D6D6D666"), labels=c("PWS", "TB", "",""))+
    ylab("Covariance")+
    guides(color = guide_legend(override.aes = list(color=c(cols[c(2,1)],"white","white"), size=2), title=element_text("Outlier", size=10)))+
    scale_x_continuous(name="Chromosome", breaks=poss$x, labels=1:26)

ggsave(paste0("../Output/COV_analysis/PWS_TB_100k_Window_Outliers.png"), width = 11, height = 5, dpi=300)

4 Run the snpEff pipeline to find annotation in the outlier regions (window+-100k)

4.1 Create a script to run SnpEff

  • Create VCF files with selected regions
#Create bed files
cv<-c("cov12","cov13","cov23")
#Prevent scientific notation in bed files
options(scipen=999)

chrsize<-read.table("../Data/vcf/chr_sizes.bed")


#The first line of bed files is often not red by vcftools so be careful
for (i in 1:3){
    df<-read.csv(paste0("../Output/COV_analysis/3pops_top1percent_outlier_cutoff.", cv[i], ".csv"))
    #add 100k
    df$start<-df$start-100000
    df$end<-df$end+100000

    # if less than 0, make it to start at 1
    df$start[df$start<0]<-1
    # adjust the end to each chr size
    for (j in 1:nrow(df)){
        ch.end<-chrsize$V3[chrsize$V1==df$chrom[j]]
        if (df$end[j]>ch.end) df$end[j]<-ch.end
    }
    
    dfp<-df[df$pop=="PWS",1:3]
    colnames(dfp)<-c('track type=bedGraph', '1','1')
    write.table(dfp, paste0("../Output/COV_analysis/PWS_outliers_",cv[i],"_new.bed"),quote = F, row.names = F, col.names = T,sep = "\t")
    dft<-df[df$pop=="TB",1:3]
    colnames(dft)<-c('track type=bedGraph', '1','1')
    write.table(dft, paste0("../Output/COV_analysis/TB_outliers_",cv[i],"_new.bed"),quote = F, row.names = F, col.names = F,sep = "\t")
    
    if (i==3){
        dfs<-df[df$pop=="SS",1:3]
        colnames(dfs)<-c('track type=bedGraph', '1','1')
        write.table(dfs, paste0("../Output/COV_analysis/SS_outliers_",cv[i],"_new.bed"),quote = F, row.names = F, col.names = F,sep = "\t")
    }
}

# Create a bash script to create vcf files with selected regions
bedfiles<-list.files("../Output/COV_analysis/", pattern="*_new.bed")

sink("../Scripts/COVscan_createVCFs.sh")
cat("#!/bin/bash \n\n")
for (i in 1:length(bedfiles)){
    fname<-gsub(".bed",'', bedfiles[i])
    cat(paste0("vcftools --gzvcf Data/new_vcf/3pops.MD2000_new.maf05.vcf.gz --bed Output/COV_analysis/", bedfiles[i], " --out Output/COV_analysis/", fname," --recode --keep-INFO-all \n"))
}
sink(NULL)  

# run the bash script 
#bash COVscan_createVCFs.sh

4.1.1 Run snpEff

#create a bash script to run snpEff

vfiles<-list.files("../Output/COV_analysis/", pattern="new.recode.vcf")

sink("~/programs/snpEff/runsnpEff_newMD2000.sh")
cat("#!/bin/bash \n\n")
for (i in 1:length(vfiles)){
    fname<-gsub("_new.recode.vcf","",vfiles[i])
    cat(paste0("java -Xmx8g -jar snpEff.jar Ch_v2.0.2.99 ~/Projects/PacHerring/Output/COV_analysis/",vfiles[i], " -stats ~/Projects/PacHerring/Output/COV_analysis/",fname,".html >  ~/Projects/PacHerring/Output/COV_analysis/Anno.",fname,".vcf \n"))
    
    #extract the annotation information
    cat(paste0("bcftools query -f '%CHROM %POS %INFO/AF %INFO/ANN\\n' ~/Projects/PacHerring/Output/COV_analysis/Anno.",fname,".vcf > ~/Projects/PacHerring/Output/COV_analysis/",fname,"_annotation \n\n"))

}
sink(NULL)  

4.2 Create summary gene files from snpEff and check overlapping genes

## Create summary files of snpEff results (gene annotations in the regions of interest) and reformat as a ShinyGo input 

#create gene list 
gfiles<-list.files("../Output/COV_analysis/", pattern="\\d.genes.txt")

for (i in 1:length(gfiles)){
    df<-read.table(paste0("../Output/COV_analysis/",gfiles[i]), sep="\t")
    df<-df[,1:7]
    colnames(df)<-c("GeneName","GeneId","TranscriptId","BioType","variants_impact_HIGH","variants_impact_LOW",  "variants_impact_MODERATE")
    
    fname<-gsub(".genes.txt","",gfiles[i])
    df<-df[df$BioType=="protein_coding",]
    genes<-unique(df$GeneId)
    sink(paste0("../Output/COV_analysis/geneIDlist_",fname,".txt"))
    cat(paste0(genes,"; "))
    sink(NULL)
}



#Annotation info from SnpEff
cv<-c("cov12","cov13","cov23")

for (c in 1:3){
    if (c!=3){
        for (p in 1:2){
            ano<-read.table(paste0("../Output/COV_analysis/",pops[p],"_outliers_",cv[c],"_annotation"), header = F)
            annotations<-data.frame()
            for (i in 1: nrow(ano)){
                anns<-unlist(strsplit(ano$V4[i], "\\,|\\|"))
                annm<-data.frame(matrix(anns,ncol = 16, byrow = TRUE))
                annm<-annm[,c(2,3,4,5,8)]
                colnames(annm)<-c("Effect","Putative_impact","Gene_name","Gene_ID","Feature type")
                annm<-annm[!duplicated(annm), ]
                annm$chr<-ano$V1[i]
                annm$pos<-ano$V2[i]
                annm$AF<- ano$V3[i]
                annotations<-rbind(annotations, annm)
            }     
            annotations<-annotations[,c(6:8,1:5)]
            annotations<-annotations[!duplicated(annotations[,c("chr","pos","Gene_ID")]),]
            write.csv(annotations, paste0("../Output/COV_analysis/GeneList_",pops[p],"_outliers_100k_",cv[c],".csv"), row.names = F)
        }
    }
    
    if (c==3){
        for (p in 1:3){
            ano<-read.table(paste0("../Output/COV_analysis/",pops[p],"_outliers_",cv[c],"_annotation"), header = F)
            annotations<-data.frame()
            for (i in 1: nrow(ano)){
                anns<-unlist(strsplit(ano$V4[i], "\\,|\\|"))
                annm<-data.frame(matrix(anns,ncol = 16, byrow = TRUE))
                annm<-annm[,c(2,3,4,5,8)]
                colnames(annm)<-c("Effect","Putative_impact","Gene_name","Gene_ID","Feature type")
                annm<-annm[!duplicated(annm), ]
                annm$chr<-ano$V1[i]
                annm$pos<-ano$V2[i]
                annm$AF<- ano$V3[i]
                annotations<-rbind(annotations, annm)
            }     
        annotations<-annotations[,c(6:8,1:5)]
        annotations<-annotations[!duplicated(annotations[,1:2]),]
        write.csv(annotations, paste0("../Output/COV_analysis/GeneList_",pops[p],"_outliers_100k_",cv[c],".csv"), row.names = F)
        }
    }
}
  • Background genes for annotation



5 Find the overlapping gene names

5.1 Between population within the same time period

# Output from snpEff
genefiles<-list.files("../Output/COV_analysis/", pattern="genes.txt")
Genes<-list()
all_gene_names<-data.frame()
for (i in 1:length(genefiles)){
    df<-read.table(paste0("../Output/COV_analysis/",genefiles[i]), skip=1, sep="\t")
    df<-df[,c(1:2)]
    colnames(df)<-c("Gene_Name","Gene_ID")
    df<-df[!duplicated(df),]
    
    fname<-gsub(".genes.txt","", genefiles[i])
    Genes[[i]]<-df
    names(Genes)[i]<-fname
    all_gene_names<-rbind(all_gene_names, df)
    write.csv(df, paste0("../Output/COV_analysis/Genenames_", fname,".csv"))
}
all_gene_names<-all_gene_names[!duplicated(all_gene_names$Gene_ID),]   
 
#1. Between populations
times<-c("cov12","cov13","cov23")
common<-list()
common_summary<-data.frame(time=times)
for (i in 1:3){
    tlist<-Genes[grep(times[i], names(Genes))]
    if (i !=3){
        common_genes<-intersect(tlist[[1]]["Gene_Name"], tlist[[2]]["Gene_Name"])
        common[[i]]<-common_genes
        names(common)[[i]]<-times[i]
        common_summary$PWS[i]<-nrow(tlist[[grep("PWS", names(tlist))]])
        common_summary$TB[i]<-nrow(tlist[[grep("TB", names(tlist))]])
        common_summary$SS[i]<-NA
        common_summary$common_PWS.TB[i]<-nrow(common_genes)
        
        pws<-tlist[[1]]["Gene_Name"]
        tb<-tlist[[2]]["Gene_Name"]
        x<-list(PWS=pws$Gene_Name,TB=tb$Gene_Name)
        ggvenn(x, fill_color = cols[c(2,1)], stroke_size = 0.5, set_name_size = 3,text_size=3)+ggtitle(times[i])
        ggsave(paste0("../Output/COV_analysis/OverlappingGenes_",times[i],".png"), width = 3, height=4.5, dpi=300)
    }
    if (i==3){
        common_summary$PWS[i]<-nrow(tlist[[grep("PWS", names(tlist))]])
        common_summary$TB[i]<- nrow(tlist[[grep("TB", names(tlist))]])
        common_summary$SS[i]<- nrow(tlist[[grep("SS", names(tlist))]])
        
        genes1<-intersect(tlist[[1]]["Gene_Name"], tlist[[3]]["Gene_Name"])
        genes2<-intersect(tlist[[1]]["Gene_Name"], tlist[[2]]["Gene_Name"])
        genes3<-intersect(tlist[[2]]["Gene_Name"], tlist[[3]]["Gene_Name"])
        genes4<-intersect(tlist[[1]]["Gene_Name"],intersect(tlist[[2]]["Gene_Name"], tlist[[3]]["Gene_Name"]))
        common_summary$common_PWS.TB[i]<-nrow(genes1)
        common_summary$common_PWS.SS[i]<-nrow(genes2)
        common_summary$common_SS.TB[i]<-nrow(genes3)
        common_summary$common3[i]<-nrow(genes4)
        k=i
        common[[k]]<-genes2
        names(common)[[k]]<-paste0(times[i],"_PWS.SS")
        k=k+1
        common[[k]]<-genes1
        names(common)[[k]]<-paste0(times[i],"_PWS.TB")
        k=k+1
        common[[k]]<-genes3
        names(common)[[k]]<-paste0(times[i],"_SS.TB")
        k=k+1
        common[[k]]<-genes4
        names(common)[[k]]<-paste0(times[i],"_3pops")
        
        pws<-tlist[[1]]["Gene_Name"]
        tb<- tlist[[3]]["Gene_Name"]
        ss<- tlist[[2]]["Gene_Name"]
        x<-list(PWS=pws$Gene_Name,TB=tb$Gene_Name, SS=ss$Gene_Name)
        ggvenn(x, fill_color = cols[c(2,1,3)], stroke_size = 0.5, set_name_size = 4,text_size=3)+ggtitle(times[i])
        ggsave(paste0("../Output/COV_analysis/OverlappingGenes_",times[i],".png"), width = 4, height=4.5, dpi=300)
        
        x1<-list(PWS=pws$Gene_Name,TB=tb$Gene_Name)
        ggvenn(x1, fill_color = cols[c(2,1)], stroke_size = 0.5, set_name_size = 3,text_size=3)+ggtitle(times[i])
        ggsave(paste0("../Output/COV_analysis/OverlappingGenes_PWS_TB_",times[i],".png"), width = 3, height=4.5, dpi=300)
        x2<-list(PWS=pws$Gene_Name,SS=ss$Gene_Name)
        ggvenn(x2, fill_color = cols[c(2,3)], stroke_size = 0.5, set_name_size = 3,text_size=3)+ggtitle(times[i])
        ggsave(paste0("../Output/COV_analysis/OverlappingGenes_PWS_SS_",times[i],".png"), width = 3, height=4.5, dpi=300)
         x3<-list(SS=ss$Gene_Name, TB=tb$Gene_Name)
        ggvenn(x3, fill_color = cols[c(3,1)], stroke_size = 0.5, set_name_size = 3,text_size=3)+ggtitle(times[i])
        ggsave(paste0("../Output/COV_analysis/OverlappingGenes_SS_TB_",times[i],".png"), width = 3, height=4.5, dpi=300)
        }
}
write.csv(common_summary, "../Output/COV_analysis/Overlapping_genes_withIntergenes_3pops_100kpad.csv")

#What are the overlapping gene IDs between populations (for ShinyGo)
for (i in 1: length(common)){
    gids<-common[[i]]
    df<-data.frame(Gene_name=gids)
    df<-merge(df, all_gene_names, by="Gene_Name")
    sink(paste0("../Output/COV_analysis/Overlapping_geneIDs_",names(common)[i],".txt"))
    cat(paste0(df$Gene_ID, ";"))
    sink(NULL)
}

5.2 Between time points within a population

#2. Between Time-Points within a population
times<-c("cov12","cov13","cov23")
pops<-c("PWS","TB")
common2<-list()
common_summary2<-data.frame(pop=rep(pops[1:2], each=4))
for (i in 1:length(pops)){
    plist<-Genes[grep(pops[i], names(Genes))]
    k=4*i-3
    #common genes between COV12 and COV13
    common_genes1<-intersect(plist[[1]]["Gene_Name"], plist[[2]]["Gene_Name"])
    common2[[k]]<-common_genes1
    names(common2)[[k]]<-paste0(pops[i],".", times[1],"_",times[2])
    common_summary2$Time[k]<-paste0(times[1],"_",times[2])
    common_summary2$no.of.genes[k]<-nrow(common_genes1) 
    
    c12<-plist[[1]]["Gene_Name"]
    c13<-plist[[2]]["Gene_Name"]
    c23<-plist[[3]]["Gene_Name"]
    x<-list(COV12=c12$Gene_Name,COV13=c13$Gene_Name, COV23=c23$Gene_Name)
    ggvenn(x, fill_color = cols[c(4,5,7)], stroke_size = 0.5, set_name_size = 4,text_size=3)+ggtitle(pops[i])
    ggsave(paste0("../Output/COV_analysis/OverlappingGenes_100kpad_",pops[i],".png"), width = 4, height=4.5, dpi=300)

    
    k=k+1
    #common genes between COV12 and COV23
    common_genes2<-intersect(plist[[1]]["Gene_Name"], plist[[3]]["Gene_Name"])
    common2[[k]]<-common_genes2
    names(common2)[[k]]<-paste0(pops[i],".", times[1],"_",times[3])
    common_summary2$Time[k]<-paste0(times[1],"_",times[3])
    common_summary2$no.of.genes[k]<-nrow(common_genes2) 
 
    k=k+1
    #common genes between COV13 and COV23
    common_genes3<-intersect(plist[[2]]["Gene_Name"], plist[[3]]["Gene_Name"])
    common2[[k]]<-common_genes3
    names(common2)[[k]]<-paste0(pops[i],".", times[2],"_",times[3])
    common_summary2$Time[k]<-paste0(times[2],"_",times[3])
    common_summary2$no.of.genes[k]<-nrow(common_genes3) 
 
    k=k+1
    #common genes among all time periods
    common_genes4<-intersect(plist[[1]]["Gene_Name"], (intersect(plist[[2]]["Gene_Name"], plist[[3]]["Gene_Name"])))
    common2[[k]]<-common_genes4
    names(common2)[[k]]<-paste0(pops[i],".all")
    common_summary2$Time[k]<-"All"
    common_summary2$no.of.genes[k]<-nrow(common_genes4) 
}
write.csv(common_summary2, "../Output/COV_analysis/Overlapping_genes_betweenTimePoints_summary.csv")


#Common gene names between time points

for (i in 1:2){
    CommonGenes<-data.frame()
    glist<-common2[grep(pops[i], names(common2))]
    for(j in 1:length(glist)){
        gids<-glist[[j]]
        if(nrow(gids)>0){
            df<-data.frame(Gene_Name=gids)
            df2<-merge(df, all_gene_names, by="Gene_Name", all.x=T)
            df2$Time<-names(glist)[j]
            sink(paste0("../Output/COV_analysis/",pops[i],"_overlap_", names(glist)[j],".txt"))
            cat(paste0(df2$Gene_ID, ";"))
            sink(NULL)
            CommonGenes<-rbind(CommonGenes, df2)
        }
    }
    write.csv(CommonGenes, paste0("../Output/COV_analysis/Overlapping_genes_100kpad_",pops[i] ,".csv"), row.names = F)
}

** The Numbers of overlapping genes between time points

pop Time no.of.genes
PWS cov12_cov13 18
PWS cov12_cov23 35
PWS cov13_cov23 18
PWS All 0
TB cov12_cov13 13
TB cov12_cov23 35
TB cov13_cov23 20
TB All 3
LS0tCnRpdGxlOiAiVGVtcG9yYWwgQ292YXJpYWNuZSBBbmFseXNpcyBQYXJ0IDEiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgICB0b2M6IHRydWUgCiAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgICAgdGhlbWU6IGx1bWVuCiAgICAgIGhpZ2hsaWdodDogdGFuZ28KICAgICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICAgIGRmX3ByaW50OiBwYWdlZAotLS0KCiogVGhpcyBub3RlYm9vayBzdW1tYXJpemVzIHRoZSByZXN1bHRzIGZyb20gY3Z0a3B5IChHZW5vbWUtd2lkZSB0ZW1wb3JhbCBjb3ZhcmlhbmNlcyBvZiBhbGxlbGUgZnJlcXVlbmNpZXMpLiAgCiogVGhlIGNvdmFyaWFuY2Ugd2luZG93IHNpemUgdXNlZCBpcyAxMDBrIChidXQgdGhlIHdpbmRvdyBzaXplcyBkaWQgbm90IGFmZmVjdCB0aGUgcmVzdWx0cykgCgpgYGB7ciBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpzb3VyY2UoIkJhc2VTY3JpcHRzLlIiKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkcGx5cikKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShnZ3Zlbm4pCmBgYAoKCiMgUGxvdHMgQ09WcyByZXN1bHRzCiogQ0lzIGZyb20gdGhlICdzdGFuZGFyZCcgYm9vdHN0cmFwIG1ldGhvZAoKYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcG9wczwtYygiUFdTIiwiVEIiLCJTUyIpCmNvdnM8LWRhdGEuZnJhbWUoKQpmb3IgKHAgaW4gMTogbGVuZ3RoKHBvcHMpKXsKICAgICNjb3ZhcmlhbmNlIG91dHB1dCBmaWxlCiAgICBjb3Y8LXJlYWQuY3N2KHBhc3RlMCgiLi4vY3Z0a19hbmFseXNpcy9NRDIwMDBfM3BvcHNfbWFmMDVfdGVtcF9jb3ZfbWF0cml4XyIscG9wc1twXSwiXzEwMGsuY3N2IikpCiAgICBjb3Y8LWNvdlssLTFdCiAgICAgICAgCiAgICAjcmVzaGFwZSB0aGUgbWF0cml4CiAgICBtYXQxPC1jb3ZbMTozLF0KICAgIG1hdDI8LWNvdls0OjYsXQogICAgICAgIAogICAgY292ZGY8LWRhdGEuZnJhbWUoKQogICAgaz0xCiAgICBmb3IgKGkgaW4gMTpucm93KG1hdDEpKXsKICAgICAgICBmb3IgKGogaW4gMTpuY29sKG1hdDEpKXsKICAgICAgICAgICAgY292ZGZbaywxXTwtbWF0MltpLGpdCiAgICAgICAgICAgIGNvdmRmW2ssMl08LW1hdDFbaSxqXQogICAgICAgICAgICBrPWsrMQogICAgICAgIH0KICAgIH0KICAgIGNvbG5hbWVzKGNvdmRmKTwtYygibGFiZWwiLCJ2YWx1ZSIpCiAgICBjb3ZkZiR2YWx1ZTwtYXMubnVtZXJpYyhjb3ZkZiR2YWx1ZSkKICAgIGNvdmFyPC1jb3ZkZltncmVwKCJjb3YiLGNvdmRmJGxhYmVsKSxdCiAgICAgICAgCiAgICAjcmVtb3ZlIHRoZSByZWR1bmRhbnQgdmFsdWVzCiAgICBpZiAocG9wc1twXSE9IlNTIikgY292YXI8LWNvdmFyWyFkdXBsaWNhdGVkKGNvdmFyWywgMl0pLF0gCiAgICBpZiAocG9wc1twXT09IlNTIikgY292YXI8LWNvdmFyW2MoMSwyLDQpLF0KICAgICAgICAKICAgICNhc3NpZ24gdGhlIHN0YXJ0aW5nIHRpbWUgcGVyaW9kIGFuZCBjb3ZlcmluZyBwZXJpb2QgdmFsdWVzCiAgICBjb3ZhciR5ZWFyPC1jKDEsMiwyKQogICAgY292YXIkc2VyaWVzPC1jKCIxOTkxIiwiMTk5MSIsIjE5OTYiKQogICAgICAgIAogICAgI2Fzc2lnbiBwb3B1bGF0aW9uIG5hbWUKICAgIGNvdmFyJGxvY2F0aW9uPC1wb3BzW3BdCiAgICAKICAgICNjb21iaW5lIGluIHRvIG9uZSBtYXRyaXgKICAgIGNvdnM8LXJiaW5kKGNvdnMsIGNvdmFyKQp9Cgpjb3ZzJHRpbWU8LXJlcChjKCJjb3YxMiIsImNvdjEzIiwiY292MjMiKSwgMykKY29sbmFtZXMoY292cylbMl08LSJjb3YiCgojIDk1JSBjb25maWRlbmNlIGludGVydmFscyAoY2FsY3VsYXRlZCBmcm9tIHRoZSAnc3RyYXBzJyByZXR1cm5lZCBmcm9tIGJvb3RzdHJhcF9jb3YyKCkgY2k9MS45NipzZChzdHJhcHMpKQp0aW1lPC1jKCJjb3YxMiIsImNvdjEzIiwiY292MjMiKQoKY292cyRjaTwtTkEKZm9yIChpIGluIDE6bGVuZ3RoKHBvcHMpKXsgIAogICAgIGlmIChpIT0zKXsKICAgICAgICBkZjwtcmVhZC5jc3YocGFzdGUwKCIuLi9jdnRrX2FuYWx5c2lzL01EMjAwMF8iLHBvcHNbaV0sIl9DSXNfMTAwa3dpbmRvdy5jc3YiKSwgaGVhZGVyPUYpCiAgICAgICAgY292cyRjaVtjb3ZzJGxvY2F0aW9uPT1wb3BzW2ldJnRpbWU9PSdjb3YxMiddPC1kZlsxLDJdCiAgICAgICAgY292cyRjaVtjb3ZzJGxvY2F0aW9uPT1wb3BzW2ldJnRpbWU9PSdjb3YxMyddPC1kZlsxLDNdCiAgICAgICAgY292cyRjaVtjb3ZzJGxvY2F0aW9uPT1wb3BzW2ldJnRpbWU9PSdjb3YyMyddPC1kZlsyLDNdCiAgICB9CiAgICBpZiAocD09MykgewogICAgICAgIGRmPC1yZWFkLmNzdihwYXN0ZTAoIi4uL2N2dGtfYW5hbHlzaXMvTUQyMDAwXyIscG9wc1tpXSwiX0NJc18xMDBrd2luZG93LmNzdiIpLCBoZWFkZXI9RikKICAgICAgICBjb3ZzJGNpW2NvdnMkbG9jYXRpb249PXBvcHNbaV0mdGltZT09J2NvdjIzJ108LWRmWzEsMl0gCiAgICB9Cn0KCndyaXRlLmNzdihjb3ZzLCIuLi9PdXRwdXQvQ09WX2FuYWx5c2lzL01EMjAwMF9HV19jb3ZhcmlhbmNlX0NJcy5jc3YiKQoKeHRleHRzPC1jKCJcdTAzOTQxOTkxLTE5OTZcbiB+IFx1MDM5NDE5OTYtMjAwNiIsICJcbiAgfiBcdTAzOTQyMDA2LTIwMTciKQoKZ2dwbG90KGRhdGE9Y292cywgYWVzKHg9eWVhciwgeT1jb3YsIGNvbG9yPWxvY2F0aW9uLCBzaGFwZT1zZXJpZXMsIGdyb3VwPWludGVyYWN0aW9uKGxvY2F0aW9uLCBzZXJpZXMpKSkrCiAgICAgICAgZ2VvbV9wb2ludChzaXplPTMsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC4xLHByZXNlcnZlID0idG90YWwiKSkrCiAgICAgICAgZ2VvbV9saW5lKGRhdGE9Y292cywgYWVzKHg9eWVhciwgeT1jb3YsY29sb3I9bG9jYXRpb24sIGdyb3VwPWludGVyYWN0aW9uKGxvY2F0aW9uLCBzZXJpZXMpKSwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjEscHJlc2VydmUgPSJ0b3RhbCIpKSsKICAgICAgICB5bGFiKCJDb3ZhcmlhbmNlIikreGxhYignJykrdGhlbWVfY2xhc3NpYygpKwogICAgICAgIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkrCiAgICAgICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCxjb2xvcj0iZ3JheTcwIiwgc2l6ZT0wLjMpKwogICAgICAgIGdlb21fZXJyb3JiYXIoYWVzKHltaW49Y292LWNpLCB5bWF4PWNvditjaSksIHdpZHRoPS4yLCBzaXplPS4yLCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuMSxwcmVzZXJ2ZSA9InRvdGFsIikpKwogICAgICAgIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9YygxNiwxNyksbGFiZWxzPWMoIlx1MDM5NCc5MS0nOTZ+IiwiXHUwMzk0Jzk2LScwNn4iKSkrCiAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoMSwyKSwgbGFiZWxzPXh0ZXh0cykrCiAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xzW2MoMiwzLDEpXSkreWxpbSgtMC4wMDM3LDAuMDAxMykKZ2dzYXZlKHBhc3RlMCgiLi4vT3V0cHV0L0NPVl9hbmFseXNpcy9NRDIwMDBfM1BvcHNfQ292X292ZXJ0aW1lX0NJZXN0aW1hdGVkLnBuZyIpLHdpZHRoID0gNC43LCBoZWlnaHQgPSAzLCBkcGk9MzAwKQogICAgCmNvdnMkdGltZTwtZmFjdG9yKGNvdnMkdGltZSwgbGV2ZWxzPWMoImNvdjEyIiwiY292MjMiLCJjb3YxMyIpKQp4dGV4dHM8LWMoIlx1MDM5NCc5MS0nOTZcbiB+IFx1MDM5NCc5Ni0nMDYiLCAiXHUwMzk0Jzk2LScwNlxuICB+IFx1MDM5NCcwNi0nMTciLCAiXHUwMzk0JzkxLSc5NlxuICB+IFx1MDM5NCcwNi0nMTciKQoKZ2dwbG90KGRhdGE9Y292cywgYWVzKHg9dGltZSwgeT1jb3YsIGNvbG9yPWxvY2F0aW9uKSkrCiAgICAgICAgZ2VvbV9wb2ludChzaXplPTMsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC4xLHByZXNlcnZlID0idG90YWwiKSkrCiAgICAgICAgeWxhYigiQ292YXJpYW5jZSIpK3hsYWIoJycpK3RoZW1lX2NsYXNzaWMoKSsKICAgICAgICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9OSkpKwogICAgICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsY29sb3I9ImdyYXk3MCIsIHNpemU9MC4zKSsKICAgICAgICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPWNvdi1jaSwgeW1heD1jb3YrY2kpLCB3aWR0aD0uMiwgc2l6ZT0uMiwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjEscHJlc2VydmUgPSJ0b3RhbCIpKSsKICAgICAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz14dGV4dHMpKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xzW2MoMiwzLDEpXSkrCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKDEuNSwyLjUpLCBjb2xvcj0iZ3JheSIsIHNpemU9MC4yKSt5bGltKC0wLjAwMzgsMC4wMDE0KQpnZ3NhdmUocGFzdGUwKCIuLi9PdXRwdXQvQ09WX2FuYWx5c2lzL01EMjAwMF8zUG9wc19Db3ZfQ0lfM3RpbWVwb2ludHMucG5nIiksd2lkdGggPSA0LjU3LCBoZWlnaHQgPSAzLCBkcGk9MzAwKQoKYGBgCgohW10oLi4vT3V0cHV0L0NPVl9hbmFseXNpcy9NRDIwMDBfM1BvcHNfQ292X292ZXJ0aW1lX0NJZXN0aW1hdGVkLnBuZykKCgoqIFNhbWUgcmVzdWx0cyBidXQgcGxvdCBkaWZmZXJlbnRseSAKCiFbXSguLi9PdXRwdXQvQ09WX2FuYWx5c2lzL01EMjAwMF8zUG9wc19Db3ZfQ0lfM3RpbWVwb2ludHMucG5nKSAgCgoKIyBGaW5kIHJlZ2lvbnMgd2l0aCBoaWdoIGNvdmFyaWFuY2VzIChvdXRsaWVyIHJlZ2lvbnMpIGluIGVhY2ggcG9wdWxhdGlvbgoKIyMgUGxvdCB0aGUgY292YXJpYW5jZXMgYWNyb3NzIHRoZSBnZW5vbWUgIAoKYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiNGaW5kIHRoZSByZWdpb25zIHdpdGggYSBoaWdoIHRlbXBvcmFsIGNvdmFyaWFuY2UgCnBvcHM8LWMoIlBXUyIsIlRCIiwiU1MiKQpldmVuczwtcGFzdGUwKCJjaHIiLHNlcSgyLDI2LCBieT0yKSkKY292Lmxpc3Q8LWxpc3QoKQpjb3ZzX2FsbDwtbGlzdCgpCms9MQpmb3IgKHAgaW4gMTogbGVuZ3RoKHBvcHMpKXsKICAgIHBvcDwtcG9wc1twXQogICAgaXY8LXJlYWQuY3N2KHBhc3RlMCgiLi4vY3Z0a19hbmFseXNpcy8zcG9wc19pbnRlcnZhbHNfMTAwa3dpbmRvdy5jc3YiKSwgcm93Lm5hbWVzID0gMSkKICAgIGlmIChwPT0zKSB7CiAgICAgICAgY292MjM8LXJlYWQuY3N2KHBhc3RlMCgiLi4vY3Z0a19hbmFseXNpcy8iLHBvcCwiX2NvdjIzX01EMjAwMF8xMDBrd2luZG93LmNzdiIpLCBoZWFkZXIgPSBGKQogICAgICAgIGNvdnM8LWNiaW5kKGl2LCBjb3YyMykKICAgICAgICBjb2xuYW1lcyhjb3ZzKVs0XTwtYygiY292MjMiKQogICAgICAgIGNvdnMkaW5kZXg9MTpucm93KGNvdnMpCiAgICAgICAgY292cyRjb2xvcjwtImNvbDEiCiAgICAgICAgY292cyRjb2xvcltjb3ZzJGNocm9tICVpbiUgZXZlbnNdPC0iY29sMiIKCiAgICAgICAgY292c1tzYXBwbHkoY292cywgaXMuaW5maW5pdGUpXSA8LSBOQQogICAgICAgIGNvdnNbc2FwcGx5KGNvdnMsIGlzLm5hbildIDwtIE5BCiAgICAgICAgCiAgICAgICAgY292Lmxpc3RbW2tdXTwtY292cwogICAgICAgIG5hbWVzKGNvdi5saXN0KVtrXTwtcG9wCiAgICAgICAgaz1rKzEKICAgICAgICAgICAgCiAgICAgICAgeTwtbWluKGNvdnMkY292MjMsIG5hLnJtPVQpCiAgICAgICAgeW1pbjwtaWZlbHNlICh5PD0tMC4xLC0wLjEsIHkpIAogICAgICAgIHltYXg8LW1heChjb3ZzJGNvdjIzLCBuYS5ybT1UKQogICAgICAgIGdncGxvdChjb3ZzLCBhZXMoeD1pbmRleCwgeT1jb3YyMywgY29sb3I9Y29sb3IpKSsKICAgICAgICAgICAgZ2VvbV9wb2ludChzaXplPTEsIGFscGhhPTAuNSkrCiAgICAgICAgICAgIHRoZW1lX2NsYXNzaWMoKSsKICAgICAgICAgICAgeWxpbSh5bWluLHltYXgpKwogICAgICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImdyYXk3MCIsInN0ZWVsYmx1ZSIpLCBndWlkZT0ibm9uZSIpKwogICAgICAgICAgICB5bGFiKCJDb3ZhcmlhbmNlIikreGxhYignQ2hyb21vc29tZScpKwogICAgICAgICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkrCiAgICAgICAgICAgIGdndGl0bGUocG9wKQogICAgICAgIGdnc2F2ZShwYXN0ZTAoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvIixwb3AsIl9NRDIwMDBfdGVtcENvdnNfYWNyb3NzR2Vub21lXzEwMGtXaW5kb3cucG5nIiksIHdpZHRoID0gOCwgaGVpZ2h0ID0gMi43LCBkcGk9MzAwKSAKICAgICAgICB9CiAgICBlbHNlIHsKICAgICAgICBjb3YxMjwtcmVhZC5jc3YocGFzdGUwKCIuLi9jdnRrX2FuYWx5c2lzLyIscG9wLCJfY292MTJfTUQyMDAwXzEwMGt3aW5kb3cuY3N2IiksIGhlYWRlciA9IEYpCiAgICAgICAgY292MjM8LXJlYWQuY3N2KHBhc3RlMCgiLi4vY3Z0a19hbmFseXNpcy8iLHBvcCwiX2NvdjIzX01EMjAwMF8xMDBrd2luZG93LmNzdiIpLCBoZWFkZXIgPSBGKQogICAgICAgIGNvdjEzPC1yZWFkLmNzdihwYXN0ZTAoIi4uL2N2dGtfYW5hbHlzaXMvIixwb3AsIl9jb3YxM19NRDIwMDBfMTAwa3dpbmRvdy5jc3YiKSwgaGVhZGVyID0gRikKICAgICAgICBjb3ZzPC1jYmluZChpdiwgY292MTIsIGNvdjIzLGNvdjEzKQogICAgICAgIGNvbG5hbWVzKGNvdnMpWzQ6Nl08LWMoImNvdjEyIiwiY292MjMiLCJjb3YxMyIpCiAgICAgICAgY292cyRpbmRleD0xOm5yb3coY292cykKICAgIAogICAgICAgIGNvdnMkY29sb3I8LSJjb2wxIgogICAgICAgIGNvdnMkY29sb3JbY292cyRjaHJvbSAlaW4lIGV2ZW5zXTwtImNvbDIiCiAgICAKICAgICAgICBjb3ZzW3NhcHBseShjb3ZzLCBpcy5pbmZpbml0ZSldIDwtIE5BCiAgICAgICAgY292c1tzYXBwbHkoY292cywgaXMubmFuKV0gPC0gTkEKICAgICAgICAKICAgICAgICBjb3YubGlzdFtba11dPC1jb3ZzCiAgICAgICAgbmFtZXMoY292Lmxpc3QpW2tdPC1wb3AKICAgICAgICBrPWsrMQogICAgICAgIGNvdnNtPC1tZWx0KGNvdnNbLGMoImluZGV4IiwiY29sb3IiLCJjb3YxMiIsImNvdjIzIiwiY292MTMiKV0sIGlkLnZhcnMgPSBjKCJpbmRleCIsICJjb2xvciIpKQogICAgICAgIHltYXg8LW1heChjb3ZzbSR2YWx1ZSwgbmEucm09VCkKICAgICAgICB5PC1taW4oY292c20kdmFsdWUsIG5hLnJtPVQpCiAgICAgICAgeW1pbjwtaWZlbHNlICh5PD0tMC4xLC0wLjEsIHkpIAogICAgICAgIGdncGxvdChjb3ZzbSwgYWVzKHg9aW5kZXgsIHk9dmFsdWUsIGNvbG9yPWNvbG9yKSkrCiAgICAgICAgICAgIGZhY2V0X3dyYXAofnZhcmlhYmxlLCBucm93PTMpKwogICAgICAgICAgICBnZW9tX3BvaW50KHNpemU9MSwgYWxwaGE9MC41KSsKICAgICAgICAgICAgdGhlbWVfY2xhc3NpYygpKwogICAgICAgICAgICB5bGltKHltaW4seW1heCkrCiAgICAgICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiZ3JheTcwIiwic3RlZWxibHVlIiksIGd1aWRlPSJub25lIikrCiAgICAgICAgICAgIHlsYWIoIkNvdmFyaWFuY2UiKSt4bGFiKCdDaHJvbW9zb21lJykrCiAgICAgICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSsKICAgICAgICAgICAgZ2d0aXRsZShwYXN0ZTAocG9wKSkKICAgICAgICBnZ3NhdmUocGFzdGUwKCIuLi9PdXRwdXQvQ09WX2FuYWx5c2lzLyIscG9wLCJfTUQyMDAwX3RlbXBDb3ZzX2Fjcm9zc0dlbm9tZV8xMDBrV2luZG93LnBuZyIpLCB3aWR0aCA9IDgsIGhlaWdodCA9IDgsIGRwaT0zMDApICAgIAogICAgfQp9CmBgYAoKIVtdKC4uL091dHB1dC9DT1ZfYW5hbHlzaXMvUFdTX01EMjAwMF90ZW1wQ292c19hY3Jvc3NHZW5vbWVfMTAwa1dpbmRvdy5wbmcpe3dpZHRoPTc1JX0KCiFbXSguLi9PdXRwdXQvQ09WX2FuYWx5c2lzL1RCX01EMjAwMF90ZW1wQ292c19hY3Jvc3NHZW5vbWVfMTAwa1dpbmRvdy5wbmcpe3dpZHRoPTc1JX0KCiFbXSguLi9PdXRwdXQvQ09WX2FuYWx5c2lzL1NTX01EMjAwMF90ZW1wQ292c19hY3Jvc3NHZW5vbWVfMTAwa1dpbmRvdy5wbmcpe3dpZHRoPTc1JX0KCgoKIyBGaW5kIHRoZSBjb3ZhcmlhbmNlIGxvd2VyIGN1dCBvZmYgdmFsdWVzIGZvciBvdXRsaWVyIGFuYWx5c2lzICh0b3AxJSkKIyMgUGxvdCB0aGUgb3V0bGllciByYW5nZXMgIApgYGB7ciBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKIyBUb3AgMSUgY292YXJpYW5jZSB2YWx1ZXMgZm9yIGFsbCBQb3AgeCBUaW1lIHBvaW50cwpjdjwtYygiY292MTIiLCJjb3YxMyIsImNvdjIzIikKY3ZyYW5nZTwtZGF0YS5mcmFtZShwb3A9YyhwYXN0ZTAocG9wc1sxOjJdLCJfIiwgY3ZbMV0pLHBhc3RlMChwb3BzWzE6Ml0sIl8iLCBjdlsyXSkscGFzdGUwKHBvcHMsIl8iLCBjdlszXSkpKQprPTEKZm9yIChpIGluIDE6bGVuZ3RoKGN2KSl7CiAgICBpZiAoaT09MXxpPT0yKXsKICAgICAgICBpZiAoaT09MSkgaz0xCiAgICAgICAgaWYgKGk9PTIpIGs9MwogICAgICAgICNQV1MKICAgICAgICBkZjE8LWNvdi5saXN0W1twYXN0ZTAoIlBXUyIpXV0KICAgICAgICBkZjE8LWRmMVtvcmRlcihkZjFbLGN2W2ldXSwgZGVjcmVhc2luZz1UKSxdCiAgICAgICAgbjwtY2VpbGluZyhucm93KGRmMSkqMC4wMSkgI3RvcDElIHJlZ2lvbgogICAgICAgIGRmMSR0b3AxPC0iTiIKICAgICAgICBkZjEkdG9wMVsxOm5dPC0iUFdTIgogICAgICAgIHJnPC1yYW5nZShkZjFbZGYxJHRvcDE9PSJQV1MiLGN2W2ldXSwgbmEucm09VCkKICAgICAgICBjdnJhbmdlW2ssIjEwMGsiXTwtcGFzdGUwKHJnWzFdLCItIixyZ1syXSkKICAgICAgICAgIAogICAgICAgICN0YgogICAgICAgIGRmMjwtY292Lmxpc3RbWyJUQiJdXQogICAgICAgIGRmMjwtZGYyW29yZGVyKGRmMlssY3ZbaV1dLCBkZWNyZWFzaW5nPVQpLF0KICAgICAgICBkZjIkdG9wMTwtIk4iCiAgICAgICAgZGYyJHRvcDFbMTpuXTwtIlRCIgogICAgICAgIHJnMjwtcmFuZ2UoZGYyW2RmMiR0b3AxPT0iVEIiLCBjdltpXV0sIG5hLnJtPVQpCiAgICAgICAgY3ZyYW5nZVsoaysxKSwiMTAwayJdPC1wYXN0ZTAocmcyWzFdLCItIixyZzJbMl0pCiAgICB9CiAgIAogICAgaWYgKGk9PTMpewogICAgICAgIGs9NQogICAgICAgICNwd3MKICAgICAgICBkZjE8LWNvdi5saXN0W1siUFdTIl1dCiAgICAgICAgZGYxPC1kZjFbLGMoImNocm9tIiwic3RhcnQiLCJlbmQiLCJjb3YyMyIpXQogICAgICAgIGRmMTwtZGYxW29yZGVyKGRmMSRjb3YyMywgZGVjcmVhc2luZz1UKSxdCiAgICAgICAgbjwtY2VpbGluZyhucm93KGRmMSkqMC4wMSkgI3RvcDElIHJlZ2lvbgogICAgICAgIGRmMSR0b3AxPC0iTiIKICAgICAgICBkZjEkdG9wMVsxOm5dPC0iUFdTIgogICAgICAgIAogICAgICAgIHJnPC1yYW5nZShkZjFbZGYxJHRvcDE9PSJQV1MiLGN2W2ldXSwgbmEucm09VCkKICAgICAgICBjdnJhbmdlW2ssIjEwMGsiXTwtcGFzdGUwKHJnWzFdLCItIixyZ1syXSkKICAgICAgICAgICAKICAgICAgICAjdGIKICAgICAgICBkZjI8LWNvdi5saXN0W1siVEIiXV0KICAgICAgICBkZjI8LWRmMlssYygiY2hyb20iLCJzdGFydCIsImVuZCIsImNvdjIzIildCiAgICAgICAgZGYyPC1kZjJbb3JkZXIoZGYyJGNvdjIzLCBkZWNyZWFzaW5nPVQpLF0KICAgICAgICBkZjIkdG9wMTwtIk4iCiAgICAgICAgZGYyJHRvcDFbMTpuXTwtIlRCIgogICAgICAgIHJnMjwtcmFuZ2UoZGYyW2RmMiR0b3AxPT0iVEIiLCBjdltpXV0sIG5hLnJtPVQpCiAgICAgICAgY3ZyYW5nZVsoaysxKSwiMTAwayJdPC1wYXN0ZTAocmcyWzFdLCItIixyZzJbMl0pCiAgICAKICAgICAgICAjc3MKICAgICAgICBkZjM8LWNvdi5saXN0W1siU1MiXV0KICAgICAgICBkZjM8LWRmM1ssYygiY2hyb20iLCJzdGFydCIsImVuZCIsImNvdjIzIildCiAgICAgICAgZGYzPC1kZjNbb3JkZXIoZGYzJGNvdjIzLCBkZWNyZWFzaW5nPVQpLF0KICAgICAgICBkZjMkdG9wMTwtIk4iCiAgICAgICAgZGYzJHRvcDFbMTpuXTwtIlNTIgogICAgICAgIHJnMzwtcmFuZ2UoZGYzW2RmMyR0b3AxPT0iU1MiLCBjdltpXV0sIG5hLnJtPVQpCiAgICAgICAgY3ZyYW5nZVsoaysyKSwiMTAwayJdPC1wYXN0ZTAocmczWzFdLCItIixyZzNbMl0pCiAgICB9Cn0KCgpjdnM8LW1lbHQoY3ZyYW5nZSwgaWQudmFycyA9ICJwb3AiKQpjdnM8LWN2cyAlPiUKICBzZXBhcmF0ZSh2YWx1ZSwgYygibG93IiwgImhpZ2giKSwgIi0iKQpjdnMkbG93PC1hcy5udW1lcmljKGN2cyRsb3cpCmN2cyRoaWdoPC1hcy5udW1lcmljKGN2cyRoaWdoKQpjdnM8LWN2cyU+JQogIHNlcGFyYXRlKHBvcCwgYygicG9wIiwgImNvdiIpLCAiXyIpCgpnZ3Bsb3QoY3ZzLCBhZXMoeD1jb3YsIHk9bG93LCBjb2xvcj1wb3ApKSsKICAgIGdlb21fcG9pbnQoc2l6ZT0zKSsKICAgIHlsYWIoIkxvd2VyIGxpbWl0IG9mIHRvcCAxJSBjb3ZhcmlhbmNlIikrCiAgICB0aGVtZV9saWdodCgpK3hsYWIoIiIpKwogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PWMoMS41LDIuNSksIGNvbG9yPSJncmF5Iikrc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xzW2MoMiwzLDEpXSkrCiAgICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKQpnZ3NhdmUoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvVGVtcENvdl9SYW5nZV9sb3dMaW1pdF9jb21wYXJpc29uX01EMjAwMC5wbmciLCB3aWR0aCA9IDUsIGhlaWdodCA9IDMsIGRwaT0zMDApCgpgYGAKCiFbXSguLi9PdXRwdXQvQ09WX2FuYWx5c2lzL1RlbXBDb3ZfUmFuZ2VfbG93TGltaXRfY29tcGFyaXNvbl9NRDIwMDAucG5nKQoKIyMgVXNlIHRoZSBoaWdoZXN0IHZhbHVlcyBmb3IgZWFjaCB0aW1lIHBlcmlvZCB0byBkZWZpbmUgb3V0bGllciByZWdpb25zIAoKYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCmxvd3M8LWFnZ3JlZ2F0ZShjdnMkbG93LCBieT1saXN0KGN2cyRjb3YpLCBtYXgpCm5hbWVzKGxvd3MpPC1jKCJjb3YiLCJsb3ciKQojbG93IGN1dG9mZiBmb3IgZWFjaCB0aW1lIHBlcmlvZCAoMTAway13aW5kb3cpCiMgICBjb3YgICAgICAgIGxvdwojMSBjb3YxMiAwLjAyOTg1Mjc4CiMyIGNvdjEzIDAuMDM1NTg5NjQKIzMgY292MjMgMC4wMzQxOTk1Mwp3cml0ZS5jc3YobG93cywgIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvQ3V0b2ZmX2NvdnNfbWF4LmNzdiIsIHJvdy5uYW1lcyA9IEYpCgojIE91dGxpZXJzIGJhc2VkIG9uIHRoZSBuZXcgbG93IGN1dC1vZmYgdmFsdWVzIDEwMGsgd2luZG93LiAKY292MTI8LWRhdGEuZnJhbWUoKQpjb3YyMzwtZGF0YS5mcmFtZSgpCmNvdjEzPC1kYXRhLmZyYW1lKCkKCmZvciAoaSBpbiAxOmxlbmd0aChjb3YubGlzdCkpewogI1BXUyBhbmQgVEIKICBpZiAoaT09MXxpPT0yKXsKICAgIGNvdnM8LWNvdi5saXN0W1tpXV0KICAgIHBvcDwtbmFtZXMoY292Lmxpc3QpW2ldCiAgCiAgICAjb3V0bGllciBjdXRvZmYgdmFsdWUKICAgIHg8LWxvd3MkbG93W2xvd3MkY292PT0iY292MTIiXQogICAgY292czEyX3RvcDwtc3Vic2V0KGNvdnMsIGNvdjEyPj14KQogICAgY292czEyX3RvcDwtY292czEyX3RvcFtvcmRlcihjb3ZzMTJfdG9wJGNocm9tLCBjb3ZzMTJfdG9wJHN0YXJ0KSxdCiAgICBjb3ZzMTJfdG9wJHBvcDwtcG9wCiAgICBjb3YxMjwtcmJpbmQoY292MTIsIGNvdnMxMl90b3ApCiAgICAKICAgIGNvdnM8LWNvdnNbb3JkZXIoY292cyRjb3YxMywgZGVjcmVhc2luZz1UKSxdCiAgICB4PC1sb3dzJGxvd1tsb3dzJGNvdj09ImNvdjEzIl0KICAgIGNvdnMxM190b3A8LXN1YnNldChjb3ZzLCBjb3YxMz49eCkKICAgIGNvdnMxM190b3A8LWNvdnMxM190b3Bbb3JkZXIoY292czEzX3RvcCRjaHJvbSwgY292czEzX3RvcCRzdGFydCksXQogICAgY292czEzX3RvcCRwb3A8LXBvcAogICAgY292MTM8LXJiaW5kKGNvdjEzLCBjb3ZzMTNfdG9wKQogICAgCiAgICBjb3ZzPC1jb3ZzW29yZGVyKGNvdnMkY292MjMsIGRlY3JlYXNpbmc9VCksXQogICAgeDwtbG93cyRsb3dbbG93cyRjb3Y9PSJjb3YyMyJdCiAgICBjb3ZzMjNfdG9wPC1zdWJzZXQoY292c1ssYygiY2hyb20iLCJzdGFydCIsImVuZCIsImNvdjIzIiwiaW5kZXgiLCJjb2xvciIpXSwgY292MjM+PXgpCiAgICBjb3ZzMjNfdG9wPC1jb3ZzMjNfdG9wW29yZGVyKGNvdnMyM190b3AkY2hyb20sIGNvdnMyM190b3Akc3RhcnQpLF0KICAgIGNvdnMyM190b3AkcG9wPC1wb3AKICAgIGNvdjIzPC1yYmluZChjb3YyMywgY292czIzX3RvcCkKIH0KIGlmIChncmVwbCgiU1MiLG5hbWVzKGNvdi5saXN0KVtpXSkpewogICAgY292czwtY292Lmxpc3RbW2ldXQogICAgCiAgICBwb3A8LWdzdWIoIl8uKyIsJycsIG5hbWVzKGNvdi5saXN0KVtpXSkKICAgIHdpbjwtZ3N1YihwYXN0ZTAocG9wLCJfIiksICcnLCBuYW1lcyhjb3YubGlzdClbaV0pCiAgICAKICAgIGNvdnM8LWNvdnNbb3JkZXIoY292cyRjb3YyMywgZGVjcmVhc2luZz1UKSxdCiAgICB4PC1sb3dzJGxvd1tsb3dzJGNvdj09ImNvdjIzIl0KICAgIGNvdnMyM190b3A8LXN1YnNldChjb3ZzLCBjb3YyMz49eCkKICAgIGNvdnMyM190b3A8LWNvdnMyM190b3Bbb3JkZXIoY292czIzX3RvcCRjaHJvbSwgY292czIzX3RvcCRzdGFydCksXQogICAgY292czIzX3RvcCRwb3A8LXBvcAogICAgY292MjM8LXJiaW5kKGNvdjIzLCBjb3ZzMjNfdG9wKQogICAgfQp9Cgp3cml0ZS5jc3YoY292MTIsICIuLi9PdXRwdXQvQ09WX2FuYWx5c2lzLzNwb3BzX3RvcDFwZXJjZW50X291dGxpZXJfY3V0b2ZmLmNvdjEyLmNzdiIscm93Lm5hbWVzID0gRikKd3JpdGUuY3N2KGNvdjIzLCAiLi4vT3V0cHV0L0NPVl9hbmFseXNpcy8zcG9wc190b3AxcGVyY2VudF9vdXRsaWVyX2N1dG9mZi5jb3YyMy5jc3YiLHJvdy5uYW1lcyA9IEYpCndyaXRlLmNzdihjb3YxMywgIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvM3BvcHNfdG9wMXBlcmNlbnRfb3V0bGllcl9jdXRvZmYuY292MTMuY3N2Iixyb3cubmFtZXMgPSBGKQpgYGAKCgojIyBDcmVhdGUgcGxvdHMgd2l0aCBkaWZmZXJlbnQgY29sb3JzIGZvciBvdXRsaWVycwojIyMgRWFjaCB0aW1lIHBlcmlvZCAgCgpgYGB7ciBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojZm9yIENPVjEyIGFuZCBDT1YxMyBmb3IgVEIgYW5kIFBXUyAoMTAwSykKY3Y8LWMoImNvdjEyIiwiY292MTMiLCJjb3YyMyIpCmZvciAoaSBpbiAxOmxlbmd0aChjdikpewogICAgaWYgKGk9PTF8aT09Mil7CiAgICAgICAgI2N1dG9mZiB2YWx1ZQogICAgICAgIHg8LWxvd3MkbG93W2xvd3MkY292PT1jdltpXV0KICAgICAgICAjUFdTCiAgICAgICAgZGYxPC1jb3YubGlzdFtbIlBXUyJdXQogICAgICAgIGRmMTwtZGYxW29yZGVyKGRmMVssY3ZbaV1dLCBkZWNyZWFzaW5nPVQpLF0KICAgICAgICBkZjEkdG9wMTwtIk4iCiAgICAgICAgZGYxJHRvcDFbZGYxWyxjdltpXV0+PXhdPC0iUFdTIgogICAgICAgIAogICAgICAgICN0YgogICAgICAgIGRmMjwtY292Lmxpc3RbWyJUQiJdXQogICAgICAgIGRmMjwtZGYyW29yZGVyKGRmMlssY3ZbaV1dLCBkZWNyZWFzaW5nPVQpLF0KICAgICAgICBkZjIkdG9wMTwtIk4iCiAgICAgICAgZGYyJHRvcDFbZGYyWyxjdltpXV0+PXhdPC0iVEIiCiAgICAgICAgCiAgICAgICAgI0NvbWJpbmUgUFdTIGFuZCBUQiB0YWJsZXMKICAgICAgICBjbzwtcmJpbmQoZGYxLCBkZjIpCiAgICAgICAgY28kY2hyb208LWZhY3RvcihjbyRjaHJvbSwgbGV2ZWxzPXBhc3RlMCgiY2hyIiwgMToyNikpCiAgICAgICAgY28kdG9wMTwtZmFjdG9yKGNvJHRvcDEsIGxldmVscz1jKCJQV1MiLCJUQiIsIk4iKSkKICAgICAgICBjb2xuYW1lcyhjbylbd2hpY2goY29sbmFtZXMoY28pPT1jdltpXSldPC0iY292IgogICAgCiAgICAgICAgeW1heDwtbWF4KGNvJGNvdiwgbmEucm09VCkKICAgICAgICAjYXNzaWduIGNvbG9ycwogICAgICAgIGNvJHRvcDE8LWFwcGx5KGNvLCAxLCBmdW5jdGlvbih4KSB7aWZlbHNlICh4Wyd0b3AxJ109PSJOIiwgeFsnY29sb3InXSwgeFsndG9wMSddKX0gKQogICAgICAgIGNvJHRvcDE8LWZhY3RvcihjbyR0b3AxLCBsZXZlbHM9YygiUFdTIiwiVEIiLCJjb2wxIiwiY29sMiIpKQogICAgICAgIAogICAgICAgICNjb3VudCB0aGUgbnVtYmVyIG9mIHNpdGVzIHBlciBjaHJvbW9zb21lcwogICAgICAgIHBvc3M8LWRhdGEuZnJhbWUoY2hyPXBhc3RlMCgiY2hyIiwxOjI2KSkKICAgICAgICBrPTEKICAgICAgICBmb3IgKGogaW4gMToyNil7CiAgICAgICAgICAgIGRmPC1kZjFbZGYxJGNocj09cGFzdGUwKCJjaHIiLGopLF0KICAgICAgICAgICAgcG9zcyRzdGFydFtqXTwtawogICAgICAgICAgICBwb3NzJGVuZFtqXTwtaytucm93KGRmKS0xCiAgICAgICAgICAgIGs9aytucm93KGRmKQogICAgICAgIH0KICAgICAgICBwb3NzJHg8LXBvc3Mkc3RhcnQrKHBvc3MkZW5kLXBvc3Mkc3RhcnQpLzIKICAgICAgICB5bWF4PC1tYXgoY28kY292LCBuYS5ybT1UKQogICAgICAgIGdncGxvdChjbywgYWVzKHg9aW5kZXgsIHk9Y292LCBjb2xvcj10b3AxKSkrCiAgICAgICAgICAgIGdlb21fcG9pbnQoc2l6ZT0wLjUpKwogICAgICAgICAgICB0aGVtZV9jbGFzc2ljKCkreWxpbSgtMC4xLHltYXgpKwogICAgICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMocGFzdGUwKGNvbHNbMl0sIkIzIikscGFzdGUwKGNvbHNbMV0sIkIzIiksIiNBOEJCQ0Q2NiIsIiNENkQ2RDY2NiIpLCBsYWJlbHM9YygiUFdTIiwgIlRCIiwgIiIsIiIpKSsKICAgICAgICAgICAgeWxhYigiQ292YXJpYW5jZSIpKwogICAgICAgICAgICBnZ3RpdGxlKHBhc3RlMCgiMTAwayB3aW5kb3cgIixjdltpXSkpKwogICAgICAgICAgICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChjb2xvcj1jKGNvbHNbMl0sIGNvbHNbMV0sIndoaXRlIiwid2hpdGUiKSwgc2l6ZT0yKSwgdGl0bGU9ZWxlbWVudF90ZXh0KCJPdXRsaWVyIFJlZ2lvbiIsIHNpemU9MTApKSkrCiAgICAgICAgICAgIHNjYWxlX3hfY29udGludW91cyhuYW1lPSJDaHJvbW9zb21lIiwgYnJlYWtzPXBvc3MkeCwgbGFiZWxzPTE6MjYpCiAgICAgICAgZ2dzYXZlKHBhc3RlMCgiLi4vT3V0cHV0L0NPVl9hbmFseXNpcy8zUG9wcy4iLGN2W2ldLCJfMTAwa19XaW5kb3dfT3V0bGllcnMucG5nIiksIHdpZHRoID0gMTAsIGhlaWdodCA9IDMuNSwgZHBpPTMwMCkKICAgIH0KICAgCiAgICBpZiAoaT09Myl7CiAgICAgICAjY3V0b2ZmIHZhbHVlCiAgICAgICAgeDwtbG93cyRsb3dbbG93cyRjb3Y9PWN2W2ldXQogICAgICAgICNQV1MKICAgICAgICBkZjE8LWNvdi5saXN0W1siUFdTIl1dCiAgICAgICAgZGYxPC1kZjFbLGMoImNocm9tIiwic3RhcnQiLCJlbmQiLCJjb3YyMyIsImluZGV4IiwiY29sb3IiKV0KICAgICAgICBkZjE8LWRmMVtvcmRlcihkZjEkY292MjMsIGRlY3JlYXNpbmc9VCksXQogICAgICAgIGRmMSR0b3AxPC0iTiIKICAgICAgICBkZjEkdG9wMVtkZjFbLGN2W2ldXT49eF08LSJQV1MiCiAgICAgICAgCiAgICAgICAgI3RiCiAgICAgICAgZGYyPC1jb3YubGlzdFtbIlRCIl1dCiAgICAgICAgZGYyPC1kZjJbLGMoImNocm9tIiwic3RhcnQiLCJlbmQiLCJjb3YyMyIsImluZGV4IiwiY29sb3IiKV0KICAgICAgICBkZjI8LWRmMltvcmRlcihkZjIkY292MjMsIGRlY3JlYXNpbmc9VCksXQogICAgICAgIGRmMiR0b3AxPC0iTiIKICAgICAgICBkZjIkdG9wMVtkZjJbLGN2W2ldXT49eF08LSJUQiIKICAgIAogICAgICAgICNzcwogICAgICAgIGRmMzwtY292Lmxpc3RbWyJTUyJdXQogICAgICAgIGRmMzwtZGYzWyxjKCJjaHJvbSIsInN0YXJ0IiwiZW5kIiwiY292MjMiLCJpbmRleCIsImNvbG9yIildCiAgICAgICAgZGYzPC1kZjNbb3JkZXIoZGYzJGNvdjIzLCBkZWNyZWFzaW5nPVQpLF0KICAgICAgICBkZjMkdG9wMTwtIk4iCiAgICAgICAgZGYzJHRvcDFbZGYzWyxjdltpXV0+PXhdPC0iU1MiCgogICAgICAgIGNvPC1yYmluZChkZjEsZGYyLGRmMykKCiAgICAgICAgY28kY2hyb208LWZhY3RvcihjbyRjaHJvbSwgbGV2ZWxzPXBhc3RlMCgiY2hyIiwgMToyNikpCiAgICAgICAgY28kdG9wMTwtZmFjdG9yKGNvJHRvcDEsIGxldmVscz1jKCJQV1MiLCJUQiIsIlNTIiwiTiIpKQogICAgICAgIHltYXg8LW1heChjbyRjb3YyMywgbmEucm09VCkKICAgICAgICAKICAgICAgICAjYXNzaWduIGNvbG9ycwogICAgICAgIGNvJHRvcDE8LWFwcGx5KGNvLCAxLCBmdW5jdGlvbih4KSB7aWZlbHNlICh4Wyd0b3AxJ109PSJOIiwgeFsnY29sb3InXSwgeFsndG9wMSddKX0gKQogICAgICAgIGNvJHRvcDE8LWZhY3RvcihjbyR0b3AxLCBsZXZlbHM9YygiUFdTIiwiVEIiLCJTUyIsImNvbDEiLCJjb2wyIikpCiAgICAgICAgI2NvdW50IHRoZSBudW1iZXIgb2Ygc2l0ZXMgcGVyIGNocm9tb3NvbWVzCiAgICAgICAgcG9zczwtZGF0YS5mcmFtZShjaHI9cGFzdGUwKCJjaHIiLDE6MjYpKQogICAgICAgIGs9MQogICAgICAgIGZvciAoaiBpbiAxOjI2KXsKICAgICAgICAgICAgZGY8LWRmMVtkZjEkY2hyPT1wYXN0ZTAoImNociIsaiksXQogICAgICAgICAgICBwb3NzJHN0YXJ0W2pdPC1rCiAgICAgICAgICAgIHBvc3MkZW5kW2pdPC1rK25yb3coZGYpLTEKICAgICAgICAgICAgaz1rK25yb3coZGYpCiAgICAgICAgfQogICAgICAgIHBvc3MkeDwtcG9zcyRzdGFydCsocG9zcyRlbmQtcG9zcyRzdGFydCkvMgogICAgICAgIHltYXg8LW1heChjbyRjb3YsIG5hLnJtPVQpCiAgICAgICAgZ2dwbG90KGNvLCBhZXMoeD1pbmRleCwgeT1jb3YyMywgY29sb3I9dG9wMSkpKwogICAgICAgICAgICBnZW9tX3BvaW50KHNpemU9MC41KSsKICAgICAgICAgICAgdGhlbWVfY2xhc3NpYygpK3lsaW0oLTAuMSx5bWF4KSsKICAgICAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKHBhc3RlMChjb2xzW2MoMiwxLDMpXSwiQjMiKSwiI0E4QkJDRDY2IiwiI0Q2RDZENjY2IiksIGxhYmVscz1jKCJQV1MiLCAiVEIiLCJTUyIsICIiLCIiKSkrCiAgICAgICAgICAgICAgICB5bGFiKCJDb3ZhcmlhbmNlIikrCiAgICAgICAgICAgICAgICBnZ3RpdGxlKHBhc3RlMCgiIDEwMGsgd2luZG93ICIsY3ZbaV0pKSsKICAgICAgICAgICAgICAgIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGNvbG9yPWMoY29sc1tjKDIsMSwzKV0sIndoaXRlIiwid2hpdGUiKSwgc2l6ZT0yKSwgdGl0bGU9ZWxlbWVudF90ZXh0KCJPdXRsaWVyICgxJSkiKSkpKwogICAgICAgICAgICBzY2FsZV94X2NvbnRpbnVvdXMobmFtZT0iQ2hyb21vc29tZSIsIGJyZWFrcz1wb3NzJHgsIGxhYmVscz0xOjI2KSsKICAgICAgICAgICAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTApKQogICAgICAgIGdnc2F2ZShwYXN0ZTAoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvM1BvcHMuY292MjNfMTAwa19XaW5kb3dfT3V0bGllcnMucG5nIiksIHdpZHRoID0gMTAsIGhlaWdodCA9IDMuNSwgZHBpPTMwMCkKICAgICAgICB9Cn0KCmBgYAoKIVtdKC4uL091dHB1dC9DT1ZfYW5hbHlzaXMvM1BvcHMuY292MTJfMTAwa19XaW5kb3dfT3V0bGllcnMucG5nKQoKIVtdKC4uL091dHB1dC9DT1ZfYW5hbHlzaXMvM1BvcHMuY292MTNfMTAwa19XaW5kb3dfT3V0bGllcnMucG5nKQoKCiFbXSguLi9PdXRwdXQvQ09WX2FuYWx5c2lzLzNQb3BzLmNvdjIzXzEwMGtfV2luZG93X091dGxpZXJzLnBuZykKCgoKIyMjIEFsbCB0aW1lIHByaW9kcyBmb3IgUFdTIGFuZCBUQgoKYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyMgUGxvdCAzIHRpbWUgcGVyaW9kcyB0b2dldGhlciBmb3IgUFdTIGFuZCBUQgpDb3Y8LWRhdGEuZnJhbWUoKQpmb3IgKGkgaW4gMTpsZW5ndGgoY3YpKXsKICAgICNjdXRvZmYgdmFsdWUKICAgIHg8LWxvd3MkbG93W2xvd3MkY292PT1jdltpXV0KICAgICNQV1MKICAgIGRmMTwtY292Lmxpc3RbWyJQV1MiXV0KICAgIGRmMTwtZGYxW29yZGVyKGRmMVssY3ZbaV1dLCBkZWNyZWFzaW5nPVQpLF0KICAgIGRmMSR0b3AxPC0iTiIKICAgIGRmMSR0b3AxW2RmMVssY3ZbaV1dPj14XTwtIlBXUyIKICAgIAogICAgI3RiCiAgICBkZjI8LWNvdi5saXN0W1siVEIiXV0KICAgIGRmMjwtZGYyW29yZGVyKGRmMlssY3ZbaV1dLCBkZWNyZWFzaW5nPVQpLF0KICAgIGRmMiR0b3AxPC0iTiIKICAgIGRmMiR0b3AxW2RmMlssY3ZbaV1dPj14XTwtIlRCIgogICAgCiAgICAjQ29tYmluZSBQV1MgYW5kIFRCIHRhYmxlcwogICAgY288LXJiaW5kKGRmMSwgZGYyKQogICAgY28kY2hyb208LWZhY3RvcihjbyRjaHJvbSwgbGV2ZWxzPXBhc3RlMCgiY2hyIiwgMToyNikpCiAgICBjb2xuYW1lcyhjbylbd2hpY2goY29sbmFtZXMoY28pPT1jdltpXSldPC0iY292IgogICAgI2Fzc2dpbiBjb2xvcnMKICAgIGNvJHRvcDE8LWFwcGx5KGNvLCAxLCBmdW5jdGlvbih4KSB7aWZlbHNlICh4Wyd0b3AxJ109PSJOIiwgeFsnY29sb3InXSwgeFsndG9wMSddKX0gKQogICAgY28kdG9wMTwtZmFjdG9yKGNvJHRvcDEsIGxldmVscz1jKCJQV1MiLCJUQiIsImNvbDEiLCJjb2wyIikpCiAgICBjbyR0aW1lPC1jdltpXQogICAgCiAgICBDb3Y8LXJiaW5kKENvdiwgY29bLGMoImluZGV4IiwgImNvdiIsInRvcDEiLCJ0aW1lIildKQp9CgojY291bnQgdGhlIG51bWJlciBvZiBzaXRlcyBwZXIgY2hyb21vc29tZXMKZGYxPC1jb3YubGlzdFtbIlBXUyJdXQpwb3NzPC1kYXRhLmZyYW1lKGNocj1wYXN0ZTAoImNociIsMToyNikpCms9MQpmb3IgKGogaW4gMToyNil7CiAgICAgICAgZGY8LWRmMVtkZjEkY2hyPT1wYXN0ZTAoImNociIsaiksXQogICAgICAgIHBvc3Mkc3RhcnRbal08LWsKICAgICAgICBwb3NzJGVuZFtqXTwtaytucm93KGRmKS0xCiAgICAgICAgaz1rK25yb3coZGYpCn0KcG9zcyR4PC1wb3NzJHN0YXJ0Kyhwb3NzJGVuZC1wb3NzJHN0YXJ0KS8yCnltYXg8LW1heChjbyRjb3YsIG5hLnJtPVQpCmdncGxvdChDb3YsIGFlcyh4PWluZGV4LCB5PWNvdiwgY29sb3I9dG9wMSkpKwogICAgZmFjZXRfd3JhcCh+dGltZSwgbmNvbD0xKSsKICAgIGdlb21fcG9pbnQoc2l6ZT0wLjUpKwogICAgdGhlbWVfY2xhc3NpYygpK3lsaW0oLTAuMSx5bWF4KSsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YyhwYXN0ZTAoY29sc1tjKDIsMSldLCJCMyIpLCIjQThCQkNENjYiLCIjRDZENkQ2NjYiKSwgbGFiZWxzPWMoIlBXUyIsICJUQiIsICIiLCIiKSkrCiAgICB5bGFiKCJDb3ZhcmlhbmNlIikrCiAgICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChjb2xvcj1jKGNvbHNbYygyLDEpXSwid2hpdGUiLCJ3aGl0ZSIpLCBzaXplPTIpLCB0aXRsZT1lbGVtZW50X3RleHQoIk91dGxpZXIiLCBzaXplPTEwKSkpKwogICAgc2NhbGVfeF9jb250aW51b3VzKG5hbWU9IkNocm9tb3NvbWUiLCBicmVha3M9cG9zcyR4LCBsYWJlbHM9MToyNikKCmdnc2F2ZShwYXN0ZTAoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvUFdTX1RCXzEwMGtfV2luZG93X091dGxpZXJzLnBuZyIpLCB3aWR0aCA9IDExLCBoZWlnaHQgPSA1LCBkcGk9MzAwKQpgYGAKCiFbXSguLi9PdXRwdXQvQ09WX2FuYWx5c2lzL1BXU19UQl8xMDBrX1dpbmRvd19PdXRsaWVycy5wbmcpCgojIFJ1biB0aGUgc25wRWZmIHBpcGVsaW5lIHRvIGZpbmQgYW5ub3RhdGlvbiBpbiB0aGUgb3V0bGllciByZWdpb25zICh3aW5kb3crLTEwMGspICAKCiMjIENyZWF0ZSBhIHNjcmlwdCB0byBydW4gU25wRWZmIAoKKiBDcmVhdGUgVkNGIGZpbGVzIHdpdGggc2VsZWN0ZWQgcmVnaW9ucwpgYGB7ciBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojQ3JlYXRlIGJlZCBmaWxlcwpjdjwtYygiY292MTIiLCJjb3YxMyIsImNvdjIzIikKI1ByZXZlbnQgc2NpZW50aWZpYyBub3RhdGlvbiBpbiBiZWQgZmlsZXMKb3B0aW9ucyhzY2lwZW49OTk5KQoKY2hyc2l6ZTwtcmVhZC50YWJsZSgiLi4vRGF0YS92Y2YvY2hyX3NpemVzLmJlZCIpCgoKI1RoZSBmaXJzdCBsaW5lIG9mIGJlZCBmaWxlcyBpcyBvZnRlbiBub3QgcmVkIGJ5IHZjZnRvb2xzIHNvIGJlIGNhcmVmdWwKZm9yIChpIGluIDE6Myl7CiAgICBkZjwtcmVhZC5jc3YocGFzdGUwKCIuLi9PdXRwdXQvQ09WX2FuYWx5c2lzLzNwb3BzX3RvcDFwZXJjZW50X291dGxpZXJfY3V0b2ZmLiIsIGN2W2ldLCAiLmNzdiIpKQogICAgI2FkZCAxMDBrCiAgICBkZiRzdGFydDwtZGYkc3RhcnQtMTAwMDAwCiAgICBkZiRlbmQ8LWRmJGVuZCsxMDAwMDAKCiAgICAjIGlmIGxlc3MgdGhhbiAwLCBtYWtlIGl0IHRvIHN0YXJ0IGF0IDEKICAgIGRmJHN0YXJ0W2RmJHN0YXJ0PDBdPC0xCiAgICAjIGFkanVzdCB0aGUgZW5kIHRvIGVhY2ggY2hyIHNpemUKICAgIGZvciAoaiBpbiAxOm5yb3coZGYpKXsKICAgICAgICBjaC5lbmQ8LWNocnNpemUkVjNbY2hyc2l6ZSRWMT09ZGYkY2hyb21bal1dCiAgICAgICAgaWYgKGRmJGVuZFtqXT5jaC5lbmQpIGRmJGVuZFtqXTwtY2guZW5kCiAgICB9CiAgICAKICAgIGRmcDwtZGZbZGYkcG9wPT0iUFdTIiwxOjNdCiAgICBjb2xuYW1lcyhkZnApPC1jKCd0cmFjayB0eXBlPWJlZEdyYXBoJywgJzEnLCcxJykKICAgIHdyaXRlLnRhYmxlKGRmcCwgcGFzdGUwKCIuLi9PdXRwdXQvQ09WX2FuYWx5c2lzL1BXU19vdXRsaWVyc18iLGN2W2ldLCJfbmV3LmJlZCIpLHF1b3RlID0gRiwgcm93Lm5hbWVzID0gRiwgY29sLm5hbWVzID0gVCxzZXAgPSAiXHQiKQogICAgZGZ0PC1kZltkZiRwb3A9PSJUQiIsMTozXQogICAgY29sbmFtZXMoZGZ0KTwtYygndHJhY2sgdHlwZT1iZWRHcmFwaCcsICcxJywnMScpCiAgICB3cml0ZS50YWJsZShkZnQsIHBhc3RlMCgiLi4vT3V0cHV0L0NPVl9hbmFseXNpcy9UQl9vdXRsaWVyc18iLGN2W2ldLCJfbmV3LmJlZCIpLHF1b3RlID0gRiwgcm93Lm5hbWVzID0gRiwgY29sLm5hbWVzID0gRixzZXAgPSAiXHQiKQogICAgCiAgICBpZiAoaT09Myl7CiAgICAgICAgZGZzPC1kZltkZiRwb3A9PSJTUyIsMTozXQogICAgICAgIGNvbG5hbWVzKGRmcyk8LWMoJ3RyYWNrIHR5cGU9YmVkR3JhcGgnLCAnMScsJzEnKQogICAgICAgIHdyaXRlLnRhYmxlKGRmcywgcGFzdGUwKCIuLi9PdXRwdXQvQ09WX2FuYWx5c2lzL1NTX291dGxpZXJzXyIsY3ZbaV0sIl9uZXcuYmVkIikscXVvdGUgPSBGLCByb3cubmFtZXMgPSBGLCBjb2wubmFtZXMgPSBGLHNlcCA9ICJcdCIpCiAgICB9Cn0KCiMgQ3JlYXRlIGEgYmFzaCBzY3JpcHQgdG8gY3JlYXRlIHZjZiBmaWxlcyB3aXRoIHNlbGVjdGVkIHJlZ2lvbnMKYmVkZmlsZXM8LWxpc3QuZmlsZXMoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvIiwgcGF0dGVybj0iKl9uZXcuYmVkIikKCnNpbmsoIi4uL1NjcmlwdHMvQ09Wc2Nhbl9jcmVhdGVWQ0ZzLnNoIikKY2F0KCIjIS9iaW4vYmFzaCBcblxuIikKZm9yIChpIGluIDE6bGVuZ3RoKGJlZGZpbGVzKSl7CiAgICBmbmFtZTwtZ3N1YigiLmJlZCIsJycsIGJlZGZpbGVzW2ldKQogICAgY2F0KHBhc3RlMCgidmNmdG9vbHMgLS1nenZjZiBEYXRhL25ld192Y2YvM3BvcHMuTUQyMDAwX25ldy5tYWYwNS52Y2YuZ3ogLS1iZWQgT3V0cHV0L0NPVl9hbmFseXNpcy8iLCBiZWRmaWxlc1tpXSwgIiAtLW91dCBPdXRwdXQvQ09WX2FuYWx5c2lzLyIsIGZuYW1lLCIgLS1yZWNvZGUgLS1rZWVwLUlORk8tYWxsIFxuIikpCn0Kc2luayhOVUxMKSAgCgojIHJ1biB0aGUgYmFzaCBzY3JpcHQgCiNiYXNoIENPVnNjYW5fY3JlYXRlVkNGcy5zaAoKYGBgCgoKIyMjIFJ1biBzbnBFZmYgCmBgYHtyIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNjcmVhdGUgYSBiYXNoIHNjcmlwdCB0byBydW4gc25wRWZmCgp2ZmlsZXM8LWxpc3QuZmlsZXMoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvIiwgcGF0dGVybj0ibmV3LnJlY29kZS52Y2YiKQoKc2luaygifi9wcm9ncmFtcy9zbnBFZmYvcnVuc25wRWZmX25ld01EMjAwMC5zaCIpCmNhdCgiIyEvYmluL2Jhc2ggXG5cbiIpCmZvciAoaSBpbiAxOmxlbmd0aCh2ZmlsZXMpKXsKICAgIGZuYW1lPC1nc3ViKCJfbmV3LnJlY29kZS52Y2YiLCIiLHZmaWxlc1tpXSkKICAgIGNhdChwYXN0ZTAoImphdmEgLVhteDhnIC1qYXIgc25wRWZmLmphciBDaF92Mi4wLjIuOTkgfi9Qcm9qZWN0cy9QYWNIZXJyaW5nL091dHB1dC9DT1ZfYW5hbHlzaXMvIix2ZmlsZXNbaV0sICIgLXN0YXRzIH4vUHJvamVjdHMvUGFjSGVycmluZy9PdXRwdXQvQ09WX2FuYWx5c2lzLyIsZm5hbWUsIi5odG1sID4gIH4vUHJvamVjdHMvUGFjSGVycmluZy9PdXRwdXQvQ09WX2FuYWx5c2lzL0Fubm8uIixmbmFtZSwiLnZjZiBcbiIpKQogICAgCiAgICAjZXh0cmFjdCB0aGUgYW5ub3RhdGlvbiBpbmZvcm1hdGlvbgogICAgY2F0KHBhc3RlMCgiYmNmdG9vbHMgcXVlcnkgLWYgJyVDSFJPTSAlUE9TICVJTkZPL0FGICVJTkZPL0FOTlxcbicgfi9Qcm9qZWN0cy9QYWNIZXJyaW5nL091dHB1dC9DT1ZfYW5hbHlzaXMvQW5uby4iLGZuYW1lLCIudmNmID4gfi9Qcm9qZWN0cy9QYWNIZXJyaW5nL091dHB1dC9DT1ZfYW5hbHlzaXMvIixmbmFtZSwiX2Fubm90YXRpb24gXG5cbiIpKQoKfQpzaW5rKE5VTEwpICAKYGBgCgoKYGBge2Jhc2ggZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KY2Qgfi9wcm9ncmFtcy9zbnBFZmYKYmFzaCBydW5zbnBFZmZfbmV3TUQyMDAwLnNoCmBgYAoKCiMjIENyZWF0ZSBzdW1tYXJ5IGdlbmUgZmlsZXMgZnJvbSBzbnBFZmYgYW5kIGNoZWNrIG92ZXJsYXBwaW5nIGdlbmVzCgpgYGB7ciBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIyBDcmVhdGUgc3VtbWFyeSBmaWxlcyBvZiBzbnBFZmYgcmVzdWx0cyAoZ2VuZSBhbm5vdGF0aW9ucyBpbiB0aGUgcmVnaW9ucyBvZiBpbnRlcmVzdCkgYW5kIHJlZm9ybWF0IGFzIGEgU2hpbnlHbyBpbnB1dCAKCiNjcmVhdGUgZ2VuZSBsaXN0IApnZmlsZXM8LWxpc3QuZmlsZXMoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvIiwgcGF0dGVybj0iXFxkLmdlbmVzLnR4dCIpCgpmb3IgKGkgaW4gMTpsZW5ndGgoZ2ZpbGVzKSl7CiAgICBkZjwtcmVhZC50YWJsZShwYXN0ZTAoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvIixnZmlsZXNbaV0pLCBzZXA9Ilx0IikKICAgIGRmPC1kZlssMTo3XQogICAgY29sbmFtZXMoZGYpPC1jKCJHZW5lTmFtZSIsIkdlbmVJZCIsIlRyYW5zY3JpcHRJZCIsIkJpb1R5cGUiLCJ2YXJpYW50c19pbXBhY3RfSElHSCIsInZhcmlhbnRzX2ltcGFjdF9MT1ciLAkidmFyaWFudHNfaW1wYWN0X01PREVSQVRFIikKICAgIAogICAgZm5hbWU8LWdzdWIoIi5nZW5lcy50eHQiLCIiLGdmaWxlc1tpXSkKICAgIGRmPC1kZltkZiRCaW9UeXBlPT0icHJvdGVpbl9jb2RpbmciLF0KICAgIGdlbmVzPC11bmlxdWUoZGYkR2VuZUlkKQogICAgc2luayhwYXN0ZTAoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvZ2VuZUlEbGlzdF8iLGZuYW1lLCIudHh0IikpCiAgICBjYXQocGFzdGUwKGdlbmVzLCI7ICIpKQogICAgc2luayhOVUxMKQp9CgoKCiNBbm5vdGF0aW9uIGluZm8gZnJvbSBTbnBFZmYKY3Y8LWMoImNvdjEyIiwiY292MTMiLCJjb3YyMyIpCgpmb3IgKGMgaW4gMTozKXsKICAgIGlmIChjIT0zKXsKICAgICAgICBmb3IgKHAgaW4gMToyKXsKICAgICAgICAgICAgYW5vPC1yZWFkLnRhYmxlKHBhc3RlMCgiLi4vT3V0cHV0L0NPVl9hbmFseXNpcy8iLHBvcHNbcF0sIl9vdXRsaWVyc18iLGN2W2NdLCJfYW5ub3RhdGlvbiIpLCBoZWFkZXIgPSBGKQogICAgICAgICAgICBhbm5vdGF0aW9uczwtZGF0YS5mcmFtZSgpCiAgICAgICAgICAgIGZvciAoaSBpbiAxOiBucm93KGFubykpewogICAgICAgICAgICAgICAgYW5uczwtdW5saXN0KHN0cnNwbGl0KGFubyRWNFtpXSwgIlxcLHxcXHwiKSkKICAgICAgICAgICAgICAgIGFubm08LWRhdGEuZnJhbWUobWF0cml4KGFubnMsbmNvbCA9IDE2LCBieXJvdyA9IFRSVUUpKQogICAgICAgICAgICAgICAgYW5ubTwtYW5ubVssYygyLDMsNCw1LDgpXQogICAgICAgICAgICAgICAgY29sbmFtZXMoYW5ubSk8LWMoIkVmZmVjdCIsIlB1dGF0aXZlX2ltcGFjdCIsIkdlbmVfbmFtZSIsIkdlbmVfSUQiLCJGZWF0dXJlIHR5cGUiKQogICAgICAgICAgICAgICAgYW5ubTwtYW5ubVshZHVwbGljYXRlZChhbm5tKSwgXQogICAgICAgICAgICAgICAgYW5ubSRjaHI8LWFubyRWMVtpXQogICAgICAgICAgICAgICAgYW5ubSRwb3M8LWFubyRWMltpXQogICAgICAgICAgICAgICAgYW5ubSRBRjwtIGFubyRWM1tpXQogICAgICAgICAgICAgICAgYW5ub3RhdGlvbnM8LXJiaW5kKGFubm90YXRpb25zLCBhbm5tKQogICAgICAgICAgICB9ICAgICAKICAgICAgICAgICAgYW5ub3RhdGlvbnM8LWFubm90YXRpb25zWyxjKDY6OCwxOjUpXQogICAgICAgICAgICBhbm5vdGF0aW9uczwtYW5ub3RhdGlvbnNbIWR1cGxpY2F0ZWQoYW5ub3RhdGlvbnNbLGMoImNociIsInBvcyIsIkdlbmVfSUQiKV0pLF0KICAgICAgICAgICAgd3JpdGUuY3N2KGFubm90YXRpb25zLCBwYXN0ZTAoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvR2VuZUxpc3RfIixwb3BzW3BdLCJfb3V0bGllcnNfMTAwa18iLGN2W2NdLCIuY3N2IiksIHJvdy5uYW1lcyA9IEYpCiAgICAgICAgfQogICAgfQogICAgCiAgICBpZiAoYz09Myl7CiAgICAgICAgZm9yIChwIGluIDE6Myl7CiAgICAgICAgICAgIGFubzwtcmVhZC50YWJsZShwYXN0ZTAoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvIixwb3BzW3BdLCJfb3V0bGllcnNfIixjdltjXSwiX2Fubm90YXRpb24iKSwgaGVhZGVyID0gRikKICAgICAgICAgICAgYW5ub3RhdGlvbnM8LWRhdGEuZnJhbWUoKQogICAgICAgICAgICBmb3IgKGkgaW4gMTogbnJvdyhhbm8pKXsKICAgICAgICAgICAgICAgIGFubnM8LXVubGlzdChzdHJzcGxpdChhbm8kVjRbaV0sICJcXCx8XFx8IikpCiAgICAgICAgICAgICAgICBhbm5tPC1kYXRhLmZyYW1lKG1hdHJpeChhbm5zLG5jb2wgPSAxNiwgYnlyb3cgPSBUUlVFKSkKICAgICAgICAgICAgICAgIGFubm08LWFubm1bLGMoMiwzLDQsNSw4KV0KICAgICAgICAgICAgICAgIGNvbG5hbWVzKGFubm0pPC1jKCJFZmZlY3QiLCJQdXRhdGl2ZV9pbXBhY3QiLCJHZW5lX25hbWUiLCJHZW5lX0lEIiwiRmVhdHVyZSB0eXBlIikKICAgICAgICAgICAgICAgIGFubm08LWFubm1bIWR1cGxpY2F0ZWQoYW5ubSksIF0KICAgICAgICAgICAgICAgIGFubm0kY2hyPC1hbm8kVjFbaV0KICAgICAgICAgICAgICAgIGFubm0kcG9zPC1hbm8kVjJbaV0KICAgICAgICAgICAgICAgIGFubm0kQUY8LSBhbm8kVjNbaV0KICAgICAgICAgICAgICAgIGFubm90YXRpb25zPC1yYmluZChhbm5vdGF0aW9ucywgYW5ubSkKICAgICAgICAgICAgfSAgICAgCiAgICAgICAgYW5ub3RhdGlvbnM8LWFubm90YXRpb25zWyxjKDY6OCwxOjUpXQogICAgICAgIGFubm90YXRpb25zPC1hbm5vdGF0aW9uc1shZHVwbGljYXRlZChhbm5vdGF0aW9uc1ssMToyXSksXQogICAgICAgIHdyaXRlLmNzdihhbm5vdGF0aW9ucywgcGFzdGUwKCIuLi9PdXRwdXQvQ09WX2FuYWx5c2lzL0dlbmVMaXN0XyIscG9wc1twXSwiX291dGxpZXJzXzEwMGtfIixjdltjXSwiLmNzdiIpLCByb3cubmFtZXMgPSBGKQogICAgICAgIH0KICAgIH0KfQoKYGBgCgoKCiogQmFja2dyb3VuZCBnZW5lcyBmb3IgYW5ub3RhdGlvbgpgYGB7cn0KIyBSdW4gc25wRWZmIHdpdGggbm9uIE1BRiBmaWx0ZXJlZCBmaWxlcyAoc25wRWZmX2JhY2tncm91bmQuc2gpCgojIS9iaW4vYmFzaCAKCiNqYXZhIC1YbXg4ZyAtamFyIHNucEVmZi5qYXIgQ2hfdjIuMC4yLjk5IC9Vc2Vycy9rYWhvdGlzdGhhbW1lci9Qcm9qZWN0cy9QYWNIZXJyaW5nL0RhdGEvbmV3X3ZjZi8gTUQyMDAwLzNwb3BzX01EMzAwMF9OUzAuNS52Y2YgLXN0YXRzIH4vUHJvamVjdHMvUGFjSGVycmluZy9EYXRhL25ld192Y2YvYmFja2dyb3VuZC9QSF9iYWNrZ3JvdW5kLmh0bWwgPiAgfi9Qcm9qZWN0cy9QYWNIZXJyaW5nL0RhdGEvbmV3X3ZjZi9iYWNrZ3JvdW5kL0Fubm8uUEhfYmFja2dyb3VuZC52Y2YgCgoKCiNjcmVhdGUgdGhlIGJhY2tncm91bmQgZ2VuZSBsaXN0IGZvciBhbm5vdGF0aW9uCmRmPC1yZWFkLnRhYmxlKCIuLi9EYXRhL3ZjZi9iYWNrZ3JvdW5kL1BIX2JhY2tncm91bmQuZ2VuZXMudHh0Iiwgc2VwPSJcdCIpCmRmPC1kZlssMTozXQpjb2xuYW1lcyhkZik8LWMoIkdlbmVOYW1lIiwiR2VuZUlkIiwiVHJhbnNjcmlwdElkIikKZ2VuZXM8LXVuaXF1ZShkZiRHZW5lSWQpCnNpbmsocGFzdGUwKCIuLi9EYXRhL3ZjZi9iYWNrZ3JvdW5kL1BIX2JhY2tncm91bmRfZ2VuZUlEbGlzdC50eHQiKSkKY2F0KHBhc3RlMChnZW5lcywiOyAiKSkKc2luayhOVUxMKQojMjc3NDMgZ2VuZXMKCmBgYAoKPGJyPgo8YnI+CgojIEZpbmQgdGhlIG92ZXJsYXBwaW5nIGdlbmUgbmFtZXMgIAojIyBCZXR3ZWVuIHBvcHVsYXRpb24gd2l0aGluIHRoZSBzYW1lIHRpbWUgcGVyaW9kICAKCmBgYHtyIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgT3V0cHV0IGZyb20gc25wRWZmCmdlbmVmaWxlczwtbGlzdC5maWxlcygiLi4vT3V0cHV0L0NPVl9hbmFseXNpcy8iLCBwYXR0ZXJuPSJnZW5lcy50eHQiKQpHZW5lczwtbGlzdCgpCmFsbF9nZW5lX25hbWVzPC1kYXRhLmZyYW1lKCkKZm9yIChpIGluIDE6bGVuZ3RoKGdlbmVmaWxlcykpewogICAgZGY8LXJlYWQudGFibGUocGFzdGUwKCIuLi9PdXRwdXQvQ09WX2FuYWx5c2lzLyIsZ2VuZWZpbGVzW2ldKSwgc2tpcD0xLCBzZXA9Ilx0IikKICAgIGRmPC1kZlssYygxOjIpXQogICAgY29sbmFtZXMoZGYpPC1jKCJHZW5lX05hbWUiLCJHZW5lX0lEIikKICAgIGRmPC1kZlshZHVwbGljYXRlZChkZiksXQogICAgCiAgICBmbmFtZTwtZ3N1YigiLmdlbmVzLnR4dCIsIiIsIGdlbmVmaWxlc1tpXSkKICAgIEdlbmVzW1tpXV08LWRmCiAgICBuYW1lcyhHZW5lcylbaV08LWZuYW1lCiAgICBhbGxfZ2VuZV9uYW1lczwtcmJpbmQoYWxsX2dlbmVfbmFtZXMsIGRmKQogICAgd3JpdGUuY3N2KGRmLCBwYXN0ZTAoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvR2VuZW5hbWVzXyIsIGZuYW1lLCIuY3N2IikpCn0KYWxsX2dlbmVfbmFtZXM8LWFsbF9nZW5lX25hbWVzWyFkdXBsaWNhdGVkKGFsbF9nZW5lX25hbWVzJEdlbmVfSUQpLF0gICAKIAojMS4gQmV0d2VlbiBwb3B1bGF0aW9ucwp0aW1lczwtYygiY292MTIiLCJjb3YxMyIsImNvdjIzIikKY29tbW9uPC1saXN0KCkKY29tbW9uX3N1bW1hcnk8LWRhdGEuZnJhbWUodGltZT10aW1lcykKZm9yIChpIGluIDE6Myl7CiAgICB0bGlzdDwtR2VuZXNbZ3JlcCh0aW1lc1tpXSwgbmFtZXMoR2VuZXMpKV0KICAgIGlmIChpICE9Myl7CiAgICAgICAgY29tbW9uX2dlbmVzPC1pbnRlcnNlY3QodGxpc3RbWzFdXVsiR2VuZV9OYW1lIl0sIHRsaXN0W1syXV1bIkdlbmVfTmFtZSJdKQogICAgICAgIGNvbW1vbltbaV1dPC1jb21tb25fZ2VuZXMKICAgICAgICBuYW1lcyhjb21tb24pW1tpXV08LXRpbWVzW2ldCiAgICAgICAgY29tbW9uX3N1bW1hcnkkUFdTW2ldPC1ucm93KHRsaXN0W1tncmVwKCJQV1MiLCBuYW1lcyh0bGlzdCkpXV0pCiAgICAgICAgY29tbW9uX3N1bW1hcnkkVEJbaV08LW5yb3codGxpc3RbW2dyZXAoIlRCIiwgbmFtZXModGxpc3QpKV1dKQogICAgICAgIGNvbW1vbl9zdW1tYXJ5JFNTW2ldPC1OQQogICAgICAgIGNvbW1vbl9zdW1tYXJ5JGNvbW1vbl9QV1MuVEJbaV08LW5yb3coY29tbW9uX2dlbmVzKQogICAgICAgIAogICAgICAgIHB3czwtdGxpc3RbWzFdXVsiR2VuZV9OYW1lIl0KICAgICAgICB0YjwtdGxpc3RbWzJdXVsiR2VuZV9OYW1lIl0KICAgICAgICB4PC1saXN0KFBXUz1wd3MkR2VuZV9OYW1lLFRCPXRiJEdlbmVfTmFtZSkKICAgICAgICBnZ3Zlbm4oeCwgZmlsbF9jb2xvciA9IGNvbHNbYygyLDEpXSwgc3Ryb2tlX3NpemUgPSAwLjUsIHNldF9uYW1lX3NpemUgPSAzLHRleHRfc2l6ZT0zKStnZ3RpdGxlKHRpbWVzW2ldKQogICAgICAgIGdnc2F2ZShwYXN0ZTAoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvT3ZlcmxhcHBpbmdHZW5lc18iLHRpbWVzW2ldLCIucG5nIiksIHdpZHRoID0gMywgaGVpZ2h0PTQuNSwgZHBpPTMwMCkKICAgIH0KICAgIGlmIChpPT0zKXsKICAgICAgICBjb21tb25fc3VtbWFyeSRQV1NbaV08LW5yb3codGxpc3RbW2dyZXAoIlBXUyIsIG5hbWVzKHRsaXN0KSldXSkKICAgICAgICBjb21tb25fc3VtbWFyeSRUQltpXTwtIG5yb3codGxpc3RbW2dyZXAoIlRCIiwgbmFtZXModGxpc3QpKV1dKQogICAgICAgIGNvbW1vbl9zdW1tYXJ5JFNTW2ldPC0gbnJvdyh0bGlzdFtbZ3JlcCgiU1MiLCBuYW1lcyh0bGlzdCkpXV0pCiAgICAgICAgCiAgICAgICAgZ2VuZXMxPC1pbnRlcnNlY3QodGxpc3RbWzFdXVsiR2VuZV9OYW1lIl0sIHRsaXN0W1szXV1bIkdlbmVfTmFtZSJdKQogICAgICAgIGdlbmVzMjwtaW50ZXJzZWN0KHRsaXN0W1sxXV1bIkdlbmVfTmFtZSJdLCB0bGlzdFtbMl1dWyJHZW5lX05hbWUiXSkKICAgICAgICBnZW5lczM8LWludGVyc2VjdCh0bGlzdFtbMl1dWyJHZW5lX05hbWUiXSwgdGxpc3RbWzNdXVsiR2VuZV9OYW1lIl0pCiAgICAgICAgZ2VuZXM0PC1pbnRlcnNlY3QodGxpc3RbWzFdXVsiR2VuZV9OYW1lIl0saW50ZXJzZWN0KHRsaXN0W1syXV1bIkdlbmVfTmFtZSJdLCB0bGlzdFtbM11dWyJHZW5lX05hbWUiXSkpCiAgICAgICAgY29tbW9uX3N1bW1hcnkkY29tbW9uX1BXUy5UQltpXTwtbnJvdyhnZW5lczEpCiAgICAgICAgY29tbW9uX3N1bW1hcnkkY29tbW9uX1BXUy5TU1tpXTwtbnJvdyhnZW5lczIpCiAgICAgICAgY29tbW9uX3N1bW1hcnkkY29tbW9uX1NTLlRCW2ldPC1ucm93KGdlbmVzMykKICAgICAgICBjb21tb25fc3VtbWFyeSRjb21tb24zW2ldPC1ucm93KGdlbmVzNCkKICAgICAgICBrPWkKICAgICAgICBjb21tb25bW2tdXTwtZ2VuZXMyCiAgICAgICAgbmFtZXMoY29tbW9uKVtba11dPC1wYXN0ZTAodGltZXNbaV0sIl9QV1MuU1MiKQogICAgICAgIGs9aysxCiAgICAgICAgY29tbW9uW1trXV08LWdlbmVzMQogICAgICAgIG5hbWVzKGNvbW1vbilbW2tdXTwtcGFzdGUwKHRpbWVzW2ldLCJfUFdTLlRCIikKICAgICAgICBrPWsrMQogICAgICAgIGNvbW1vbltba11dPC1nZW5lczMKICAgICAgICBuYW1lcyhjb21tb24pW1trXV08LXBhc3RlMCh0aW1lc1tpXSwiX1NTLlRCIikKICAgICAgICBrPWsrMQogICAgICAgIGNvbW1vbltba11dPC1nZW5lczQKICAgICAgICBuYW1lcyhjb21tb24pW1trXV08LXBhc3RlMCh0aW1lc1tpXSwiXzNwb3BzIikKICAgICAgICAKICAgICAgICBwd3M8LXRsaXN0W1sxXV1bIkdlbmVfTmFtZSJdCiAgICAgICAgdGI8LSB0bGlzdFtbM11dWyJHZW5lX05hbWUiXQogICAgICAgIHNzPC0gdGxpc3RbWzJdXVsiR2VuZV9OYW1lIl0KICAgICAgICB4PC1saXN0KFBXUz1wd3MkR2VuZV9OYW1lLFRCPXRiJEdlbmVfTmFtZSwgU1M9c3MkR2VuZV9OYW1lKQogICAgICAgIGdndmVubih4LCBmaWxsX2NvbG9yID0gY29sc1tjKDIsMSwzKV0sIHN0cm9rZV9zaXplID0gMC41LCBzZXRfbmFtZV9zaXplID0gNCx0ZXh0X3NpemU9MykrZ2d0aXRsZSh0aW1lc1tpXSkKICAgICAgICBnZ3NhdmUocGFzdGUwKCIuLi9PdXRwdXQvQ09WX2FuYWx5c2lzL092ZXJsYXBwaW5nR2VuZXNfIix0aW1lc1tpXSwiLnBuZyIpLCB3aWR0aCA9IDQsIGhlaWdodD00LjUsIGRwaT0zMDApCiAgICAgICAgCiAgICAgICAgeDE8LWxpc3QoUFdTPXB3cyRHZW5lX05hbWUsVEI9dGIkR2VuZV9OYW1lKQogICAgICAgIGdndmVubih4MSwgZmlsbF9jb2xvciA9IGNvbHNbYygyLDEpXSwgc3Ryb2tlX3NpemUgPSAwLjUsIHNldF9uYW1lX3NpemUgPSAzLHRleHRfc2l6ZT0zKStnZ3RpdGxlKHRpbWVzW2ldKQogICAgICAgIGdnc2F2ZShwYXN0ZTAoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvT3ZlcmxhcHBpbmdHZW5lc19QV1NfVEJfIix0aW1lc1tpXSwiLnBuZyIpLCB3aWR0aCA9IDMsIGhlaWdodD00LjUsIGRwaT0zMDApCiAgICAgICAgeDI8LWxpc3QoUFdTPXB3cyRHZW5lX05hbWUsU1M9c3MkR2VuZV9OYW1lKQogICAgICAgIGdndmVubih4MiwgZmlsbF9jb2xvciA9IGNvbHNbYygyLDMpXSwgc3Ryb2tlX3NpemUgPSAwLjUsIHNldF9uYW1lX3NpemUgPSAzLHRleHRfc2l6ZT0zKStnZ3RpdGxlKHRpbWVzW2ldKQogICAgICAgIGdnc2F2ZShwYXN0ZTAoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvT3ZlcmxhcHBpbmdHZW5lc19QV1NfU1NfIix0aW1lc1tpXSwiLnBuZyIpLCB3aWR0aCA9IDMsIGhlaWdodD00LjUsIGRwaT0zMDApCiAgICAgICAgIHgzPC1saXN0KFNTPXNzJEdlbmVfTmFtZSwgVEI9dGIkR2VuZV9OYW1lKQogICAgICAgIGdndmVubih4MywgZmlsbF9jb2xvciA9IGNvbHNbYygzLDEpXSwgc3Ryb2tlX3NpemUgPSAwLjUsIHNldF9uYW1lX3NpemUgPSAzLHRleHRfc2l6ZT0zKStnZ3RpdGxlKHRpbWVzW2ldKQogICAgICAgIGdnc2F2ZShwYXN0ZTAoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvT3ZlcmxhcHBpbmdHZW5lc19TU19UQl8iLHRpbWVzW2ldLCIucG5nIiksIHdpZHRoID0gMywgaGVpZ2h0PTQuNSwgZHBpPTMwMCkKICAgICAgICB9Cn0Kd3JpdGUuY3N2KGNvbW1vbl9zdW1tYXJ5LCAiLi4vT3V0cHV0L0NPVl9hbmFseXNpcy9PdmVybGFwcGluZ19nZW5lc193aXRoSW50ZXJnZW5lc18zcG9wc18xMDBrcGFkLmNzdiIpCgojV2hhdCBhcmUgdGhlIG92ZXJsYXBwaW5nIGdlbmUgSURzIGJldHdlZW4gcG9wdWxhdGlvbnMgKGZvciBTaGlueUdvKQpmb3IgKGkgaW4gMTogbGVuZ3RoKGNvbW1vbikpewogICAgZ2lkczwtY29tbW9uW1tpXV0KICAgIGRmPC1kYXRhLmZyYW1lKEdlbmVfbmFtZT1naWRzKQogICAgZGY8LW1lcmdlKGRmLCBhbGxfZ2VuZV9uYW1lcywgYnk9IkdlbmVfTmFtZSIpCiAgICBzaW5rKHBhc3RlMCgiLi4vT3V0cHV0L0NPVl9hbmFseXNpcy9PdmVybGFwcGluZ19nZW5lSURzXyIsbmFtZXMoY29tbW9uKVtpXSwiLnR4dCIpKQogICAgY2F0KHBhc3RlMChkZiRHZW5lX0lELCAiOyIpKQogICAgc2luayhOVUxMKQp9CgpgYGAKCiFbXSguLi9PdXRwdXQvQ09WX2FuYWx5c2lzL092ZXJsYXBwaW5nR2VuZXNfY292MTIucG5nKXt3aWR0aD01MCV9IAoKIVtdKC4uL091dHB1dC9DT1ZfYW5hbHlzaXMvT3ZlcmxhcHBpbmdHZW5lc19jb3YyMy5wbmcpe3dpZHRoPTUwJX0gCgohW10oLi4vT3V0cHV0L0NPVl9hbmFseXNpcy9PdmVybGFwcGluZ0dlbmVzX2NvdjIzLnBuZyl7d2lkdGg9NjUlfSAKCgojIyBCZXR3ZWVuIHRpbWUgcG9pbnRzIHdpdGhpbiBhIHBvcHVsYXRpb24gIAoKYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIzIuIEJldHdlZW4gVGltZS1Qb2ludHMgd2l0aGluIGEgcG9wdWxhdGlvbgp0aW1lczwtYygiY292MTIiLCJjb3YxMyIsImNvdjIzIikKcG9wczwtYygiUFdTIiwiVEIiKQpjb21tb24yPC1saXN0KCkKY29tbW9uX3N1bW1hcnkyPC1kYXRhLmZyYW1lKHBvcD1yZXAocG9wc1sxOjJdLCBlYWNoPTQpKQpmb3IgKGkgaW4gMTpsZW5ndGgocG9wcykpewogICAgcGxpc3Q8LUdlbmVzW2dyZXAocG9wc1tpXSwgbmFtZXMoR2VuZXMpKV0KICAgIGs9NCppLTMKICAgICNjb21tb24gZ2VuZXMgYmV0d2VlbiBDT1YxMiBhbmQgQ09WMTMKICAgIGNvbW1vbl9nZW5lczE8LWludGVyc2VjdChwbGlzdFtbMV1dWyJHZW5lX05hbWUiXSwgcGxpc3RbWzJdXVsiR2VuZV9OYW1lIl0pCiAgICBjb21tb24yW1trXV08LWNvbW1vbl9nZW5lczEKICAgIG5hbWVzKGNvbW1vbjIpW1trXV08LXBhc3RlMChwb3BzW2ldLCIuIiwgdGltZXNbMV0sIl8iLHRpbWVzWzJdKQogICAgY29tbW9uX3N1bW1hcnkyJFRpbWVba108LXBhc3RlMCh0aW1lc1sxXSwiXyIsdGltZXNbMl0pCiAgICBjb21tb25fc3VtbWFyeTIkbm8ub2YuZ2VuZXNba108LW5yb3coY29tbW9uX2dlbmVzMSkgCiAgICAKICAgIGMxMjwtcGxpc3RbWzFdXVsiR2VuZV9OYW1lIl0KICAgIGMxMzwtcGxpc3RbWzJdXVsiR2VuZV9OYW1lIl0KICAgIGMyMzwtcGxpc3RbWzNdXVsiR2VuZV9OYW1lIl0KICAgIHg8LWxpc3QoQ09WMTI9YzEyJEdlbmVfTmFtZSxDT1YxMz1jMTMkR2VuZV9OYW1lLCBDT1YyMz1jMjMkR2VuZV9OYW1lKQogICAgZ2d2ZW5uKHgsIGZpbGxfY29sb3IgPSBjb2xzW2MoNCw1LDcpXSwgc3Ryb2tlX3NpemUgPSAwLjUsIHNldF9uYW1lX3NpemUgPSA0LHRleHRfc2l6ZT0zKStnZ3RpdGxlKHBvcHNbaV0pCiAgICBnZ3NhdmUocGFzdGUwKCIuLi9PdXRwdXQvQ09WX2FuYWx5c2lzL092ZXJsYXBwaW5nR2VuZXNfMTAwa3BhZF8iLHBvcHNbaV0sIi5wbmciKSwgd2lkdGggPSA0LCBoZWlnaHQ9NC41LCBkcGk9MzAwKQoKICAgIAogICAgaz1rKzEKICAgICNjb21tb24gZ2VuZXMgYmV0d2VlbiBDT1YxMiBhbmQgQ09WMjMKICAgIGNvbW1vbl9nZW5lczI8LWludGVyc2VjdChwbGlzdFtbMV1dWyJHZW5lX05hbWUiXSwgcGxpc3RbWzNdXVsiR2VuZV9OYW1lIl0pCiAgICBjb21tb24yW1trXV08LWNvbW1vbl9nZW5lczIKICAgIG5hbWVzKGNvbW1vbjIpW1trXV08LXBhc3RlMChwb3BzW2ldLCIuIiwgdGltZXNbMV0sIl8iLHRpbWVzWzNdKQogICAgY29tbW9uX3N1bW1hcnkyJFRpbWVba108LXBhc3RlMCh0aW1lc1sxXSwiXyIsdGltZXNbM10pCiAgICBjb21tb25fc3VtbWFyeTIkbm8ub2YuZ2VuZXNba108LW5yb3coY29tbW9uX2dlbmVzMikgCiAKICAgIGs9aysxCiAgICAjY29tbW9uIGdlbmVzIGJldHdlZW4gQ09WMTMgYW5kIENPVjIzCiAgICBjb21tb25fZ2VuZXMzPC1pbnRlcnNlY3QocGxpc3RbWzJdXVsiR2VuZV9OYW1lIl0sIHBsaXN0W1szXV1bIkdlbmVfTmFtZSJdKQogICAgY29tbW9uMltba11dPC1jb21tb25fZ2VuZXMzCiAgICBuYW1lcyhjb21tb24yKVtba11dPC1wYXN0ZTAocG9wc1tpXSwiLiIsIHRpbWVzWzJdLCJfIix0aW1lc1szXSkKICAgIGNvbW1vbl9zdW1tYXJ5MiRUaW1lW2tdPC1wYXN0ZTAodGltZXNbMl0sIl8iLHRpbWVzWzNdKQogICAgY29tbW9uX3N1bW1hcnkyJG5vLm9mLmdlbmVzW2tdPC1ucm93KGNvbW1vbl9nZW5lczMpIAogCiAgICBrPWsrMQogICAgI2NvbW1vbiBnZW5lcyBhbW9uZyBhbGwgdGltZSBwZXJpb2RzCiAgICBjb21tb25fZ2VuZXM0PC1pbnRlcnNlY3QocGxpc3RbWzFdXVsiR2VuZV9OYW1lIl0sIChpbnRlcnNlY3QocGxpc3RbWzJdXVsiR2VuZV9OYW1lIl0sIHBsaXN0W1szXV1bIkdlbmVfTmFtZSJdKSkpCiAgICBjb21tb24yW1trXV08LWNvbW1vbl9nZW5lczQKICAgIG5hbWVzKGNvbW1vbjIpW1trXV08LXBhc3RlMChwb3BzW2ldLCIuYWxsIikKICAgIGNvbW1vbl9zdW1tYXJ5MiRUaW1lW2tdPC0iQWxsIgogICAgY29tbW9uX3N1bW1hcnkyJG5vLm9mLmdlbmVzW2tdPC1ucm93KGNvbW1vbl9nZW5lczQpIAp9CndyaXRlLmNzdihjb21tb25fc3VtbWFyeTIsICIuLi9PdXRwdXQvQ09WX2FuYWx5c2lzL092ZXJsYXBwaW5nX2dlbmVzX2JldHdlZW5UaW1lUG9pbnRzX3N1bW1hcnkuY3N2IikKCgojQ29tbW9uIGdlbmUgbmFtZXMgYmV0d2VlbiB0aW1lIHBvaW50cwoKZm9yIChpIGluIDE6Mil7CiAgICBDb21tb25HZW5lczwtZGF0YS5mcmFtZSgpCiAgICBnbGlzdDwtY29tbW9uMltncmVwKHBvcHNbaV0sIG5hbWVzKGNvbW1vbjIpKV0KICAgIGZvcihqIGluIDE6bGVuZ3RoKGdsaXN0KSl7CiAgICAgICAgZ2lkczwtZ2xpc3RbW2pdXQogICAgICAgIGlmKG5yb3coZ2lkcyk+MCl7CiAgICAgICAgICAgIGRmPC1kYXRhLmZyYW1lKEdlbmVfTmFtZT1naWRzKQogICAgICAgICAgICBkZjI8LW1lcmdlKGRmLCBhbGxfZ2VuZV9uYW1lcywgYnk9IkdlbmVfTmFtZSIsIGFsbC54PVQpCiAgICAgICAgICAgIGRmMiRUaW1lPC1uYW1lcyhnbGlzdClbal0KICAgICAgICAgICAgc2luayhwYXN0ZTAoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvIixwb3BzW2ldLCJfb3ZlcmxhcF8iLCBuYW1lcyhnbGlzdClbal0sIi50eHQiKSkKICAgICAgICAgICAgY2F0KHBhc3RlMChkZjIkR2VuZV9JRCwgIjsiKSkKICAgICAgICAgICAgc2luayhOVUxMKQogICAgICAgICAgICBDb21tb25HZW5lczwtcmJpbmQoQ29tbW9uR2VuZXMsIGRmMikKICAgICAgICB9CiAgICB9CiAgICB3cml0ZS5jc3YoQ29tbW9uR2VuZXMsIHBhc3RlMCgiLi4vT3V0cHV0L0NPVl9hbmFseXNpcy9PdmVybGFwcGluZ19nZW5lc18xMDBrcGFkXyIscG9wc1tpXSAsIi5jc3YiKSwgcm93Lm5hbWVzID0gRikKfQoKYGBgCgohW10oLi4vT3V0cHV0L0NPVl9hbmFseXNpcy9PdmVybGFwcGluZ0dlbmVzXzEwMGtwYWRfUFdTLnBuZyl7d2lkdGg9NjUlfQohW10oLi4vT3V0cHV0L0NPVl9hbmFseXNpcy9PdmVybGFwcGluZ0dlbmVzXzEwMGtwYWRfVEIucG5nKXt3aWR0aD02NSV9CgoKKiogVGhlIE51bWJlcnMgb2Ygb3ZlcmxhcHBpbmcgZ2VuZXMgYmV0d2VlbiB0aW1lIHBvaW50cwoKYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIFN1bW1hcnkgdGFibGUKY29tbW9uX2dlbmVzMjwtcmVhZC5jc3YoIi4uL091dHB1dC9DT1ZfYW5hbHlzaXMvT3ZlcmxhcHBpbmdfZ2VuZXNfYmV0d2VlblRpbWVQb2ludHNfc3VtbWFyeS5jc3YiLCByb3cubmFtZXMgPSAxKQprbml0cjo6a2FibGUoY29tbW9uX2dlbmVzMikKCmBgYAoKCg==